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

Commit

Permalink
Merge pull request #88 from mathieucarbou/issue-86
Browse files Browse the repository at this point in the history
Fix header handling versus internal handling of content-type and content-length
  • Loading branch information
mathieucarbou authored Sep 3, 2024
2 parents 4f25782 + f9cfba3 commit 74e0008
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 39 deletions.
7 changes: 5 additions & 2 deletions examples/SimpleServer/SimpleServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ void setup() {
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->method() == HTTP_HEAD) {
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
response->setContentLength(1024); // myFile.getSize()
response->addHeader("Accept-Ranges", "bytes");
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
response->addHeader(asyncsrv::T_Content_Length, 10);
response->setContentLength(1024); // overrides previous one
response->addHeader(asyncsrv::T_Content_Type, "foo");
response->setContentType("application/octet-stream"); // overrides previous one
// ...
request->send(response);
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/AsyncEventSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) {
}

void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
String out = _assembleHead(request->version());
String out;
_assembleHead(out, request->version());
request->client()->write(out.c_str(), _headLength);
_state = RESPONSE_WAIT_ACK;
}
Expand Down
3 changes: 2 additions & 1 deletion src/AsyncWebSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1192,7 +1192,8 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) {
request->client()->close(true);
return;
}
String out(_assembleHead(request->version()));
String out;
_assembleHead(out, request->version());
request->client()->write(out.c_str(), _headLength);
_state = RESPONSE_WAIT_ACK;
}
Expand Down
17 changes: 15 additions & 2 deletions src/ESPAsyncWebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ class AsyncWebHeader {
const String& name() const { return _name; }
const String& value() const { return _value; }
String toString() const {
String str = _name;
String str;
str.reserve(_name.length() + _value.length() + 2);
str.concat(_name);
str.concat((char)0x3a);
str.concat((char)0x20);
str.concat(_value);
Expand Down Expand Up @@ -583,8 +585,19 @@ class AsyncWebServerResponse {
virtual void setContentType(const char* type);
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true);
bool addHeader(const String& name, const String& value, bool replaceExisting = true) { return addHeader(name.c_str(), value.c_str(), replaceExisting); }
bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); }
bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); }
virtual bool removeHeader(const char* name);
virtual String _assembleHead(uint8_t version);
virtual const AsyncWebHeader* getHeader(const char* name) const;

[[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]]
String _assembleHead(uint8_t version) {
String buffer;
_assembleHead(buffer, version);
return buffer;
}
virtual void _assembleHead(String& buffer, uint8_t version);

virtual bool _started() const;
virtual bool _finished() const;
virtual bool _failed() const;
Expand Down
1 change: 0 additions & 1 deletion src/WebRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,6 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {

const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });

return (iter == std::end(_headers)) ? nullptr : &(*iter);
}

Expand Down
84 changes: 52 additions & 32 deletions src/WebResponses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
return T_HTTP_CODE_ANY;
}
}
#else // ESP8266
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code)
{
#else // ESP8266
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) {
case 100:
return FPSTR(T_HTTP_CODE_100);
Expand Down Expand Up @@ -230,12 +229,12 @@ void AsyncWebServerResponse::setCode(int code) {
}

void AsyncWebServerResponse::setContentLength(size_t len) {
if (_state == RESPONSE_SETUP)
if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true))
_contentLength = len;
}

void AsyncWebServerResponse::setContentType(const char* type) {
if (_state == RESPONSE_SETUP)
if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true))
_contentType = type;
}

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

const AsyncWebHeader* AsyncWebServerResponse::getHeader(const char* name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
return (iter == std::end(_headers)) ? nullptr : &(*iter);
}

bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool replaceExisting) {
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
if (i->name().equalsIgnoreCase(name)) {
Expand All @@ -268,41 +272,56 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool
return true;
}

String AsyncWebServerResponse::_assembleHead(uint8_t version) {
void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
if (version) {
addHeader(T_Accept_Ranges, T_none, false);
if (_chunked)
addHeader(T_Transfer_Encoding, T_chunked, false);
}
String out;
constexpr size_t bufSize = 300;
char buf[bufSize];

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

if (_sendContentLength) {
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
out.concat(buf);
}
if (_contentType.length()) {
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
out.concat(buf);
}
if (_contentType.length())
addHeader(T_Content_Type, _contentType.c_str(), false);

// precompute buffer size to avoid reallocations by String class
size_t len = 0;
len += 50; // HTTP/1.1 200 <reason>\r\n
for (const auto& header : _headers)
len += header.name().length() + header.value().length() + 4;

// prepare buffer
buffer.reserve(len);

// HTTP header
#ifdef ESP8266
buffer.concat(PSTR("HTTP/1."));
#else
buffer.concat("HTTP/1.");
#endif
buffer.concat(version);
buffer.concat(' ');
buffer.concat(_code);
buffer.concat(' ');
buffer.concat(responseCodeToString(_code));
buffer.concat(T_rn);

// Add headers
for (const auto& header : _headers) {
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
out.concat(buf);
buffer.concat(header.name());
#ifdef ESP8266
buffer.concat(PSTR(": "));
#else
buffer.concat(": ");
#endif
buffer.concat(header.value());
buffer.concat(T_rn);
}
_headers.clear();

out.concat(T_rn);
_headLength = out.length();
return out;
buffer.concat(T_rn);
_headLength = buffer.length();
}

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

void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) {
_state = RESPONSE_HEADERS;
String out = _assembleHead(request->version());
String out;
_assembleHead(out, request->version());
size_t outLen = out.length();
size_t space = request->client()->space();
if (!_contentLength && space >= outLen) {
Expand Down Expand Up @@ -411,7 +431,7 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _c

void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) {
addHeader(T_Connection, T_close, false);
_head = _assembleHead(request->version());
_assembleHead(_head, request->version());
_state = RESPONSE_HEADERS;
_ack(request, 0, 0);
}
Expand Down Expand Up @@ -635,9 +655,9 @@ AsyncFileResponse::~AsyncFileResponse() {
void AsyncFileResponse::_setContentTypeFromPath(const String& path) {
#if HAVE_EXTERN_GET_Content_Type_FUNCTION
#ifndef ESP8266
extern const char* getContentType(const String& path);
extern const char* getContentType(const String& path);
#else
extern const __FlashStringHelper* getContentType(const String& path);
extern const __FlashStringHelper* getContentType(const String& path);
#endif
_contentType = getContentType(path);
#else
Expand Down

0 comments on commit 74e0008

Please sign in to comment.