mirror of
https://github.com/me-no-dev/ESPAsyncWebServer.git
synced 2025-09-28 23:30:55 +02:00
Fix header handling versus internal handling of content-type and content-length (Fix #86)
This commit is contained in:
@@ -73,8 +73,11 @@ void setup() {
|
|||||||
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->method() == HTTP_HEAD) {
|
if (request->method() == HTTP_HEAD) {
|
||||||
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
|
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
|
||||||
response->setContentLength(1024); // myFile.getSize()
|
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
|
||||||
response->addHeader("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);
|
request->send(response);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -392,7 +392,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
|
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
String out = _assembleHead(request->version());
|
String out;
|
||||||
|
_assembleHead(out, request->version());
|
||||||
request->client()->write(out.c_str(), _headLength);
|
request->client()->write(out.c_str(), _headLength);
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
}
|
}
|
||||||
|
@@ -1192,7 +1192,8 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) {
|
|||||||
request->client()->close(true);
|
request->client()->close(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String out(_assembleHead(request->version()));
|
String out;
|
||||||
|
_assembleHead(out, request->version());
|
||||||
request->client()->write(out.c_str(), _headLength);
|
request->client()->write(out.c_str(), _headLength);
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
}
|
}
|
||||||
|
@@ -155,7 +155,9 @@ class AsyncWebHeader {
|
|||||||
const String& name() const { return _name; }
|
const String& name() const { return _name; }
|
||||||
const String& value() const { return _value; }
|
const String& value() const { return _value; }
|
||||||
String toString() const {
|
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)0x3a);
|
||||||
str.concat((char)0x20);
|
str.concat((char)0x20);
|
||||||
str.concat(_value);
|
str.concat(_value);
|
||||||
@@ -583,8 +585,19 @@ class AsyncWebServerResponse {
|
|||||||
virtual void setContentType(const char* type);
|
virtual void setContentType(const char* type);
|
||||||
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true);
|
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 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 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 _started() const;
|
||||||
virtual bool _finished() const;
|
virtual bool _finished() const;
|
||||||
virtual bool _failed() const;
|
virtual bool _failed() const;
|
||||||
|
@@ -624,7 +624,6 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {
|
|||||||
|
|
||||||
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) 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); });
|
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);
|
return (iter == std::end(_headers)) ? nullptr : &(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -126,8 +126,7 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else // ESP8266
|
#else // ESP8266
|
||||||
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code)
|
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
|
||||||
{
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 100:
|
case 100:
|
||||||
return FPSTR(T_HTTP_CODE_100);
|
return FPSTR(T_HTTP_CODE_100);
|
||||||
@@ -230,12 +229,12 @@ void AsyncWebServerResponse::setCode(int code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentLength(size_t len) {
|
void AsyncWebServerResponse::setContentLength(size_t len) {
|
||||||
if (_state == RESPONSE_SETUP)
|
if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true))
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentType(const char* type) {
|
void AsyncWebServerResponse::setContentType(const char* type) {
|
||||||
if (_state == RESPONSE_SETUP)
|
if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true))
|
||||||
_contentType = type;
|
_contentType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +248,11 @@ bool AsyncWebServerResponse::removeHeader(const char* name) {
|
|||||||
return false;
|
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) {
|
bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool replaceExisting) {
|
||||||
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
|
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
|
||||||
if (i->name().equalsIgnoreCase(name)) {
|
if (i->name().equalsIgnoreCase(name)) {
|
||||||
@@ -268,41 +272,56 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String AsyncWebServerResponse::_assembleHead(uint8_t version) {
|
void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
|
||||||
if (version) {
|
if (version) {
|
||||||
addHeader(T_Accept_Ranges, T_none, false);
|
addHeader(T_Accept_Ranges, T_none, false);
|
||||||
if (_chunked)
|
if (_chunked)
|
||||||
addHeader(T_Transfer_Encoding, T_chunked, false);
|
addHeader(T_Transfer_Encoding, T_chunked, false);
|
||||||
}
|
}
|
||||||
String out;
|
|
||||||
constexpr size_t bufSize = 300;
|
|
||||||
char buf[bufSize];
|
|
||||||
|
|
||||||
#ifndef ESP8266
|
if (_sendContentLength)
|
||||||
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, responseCodeToString(_code));
|
addHeader(T_Content_Length, String(_contentLength), false);
|
||||||
|
|
||||||
|
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
|
#else
|
||||||
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, String(responseCodeToString(_code)).c_str());
|
buffer.concat("HTTP/1.");
|
||||||
#endif
|
#endif
|
||||||
out.concat(buf);
|
buffer.concat(version);
|
||||||
|
buffer.concat(' ');
|
||||||
if (_sendContentLength) {
|
buffer.concat(_code);
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
buffer.concat(' ');
|
||||||
out.concat(buf);
|
buffer.concat(responseCodeToString(_code));
|
||||||
}
|
buffer.concat(T_rn);
|
||||||
if (_contentType.length()) {
|
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
|
||||||
out.concat(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Add headers
|
||||||
for (const auto& header : _headers) {
|
for (const auto& header : _headers) {
|
||||||
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
|
buffer.concat(header.name());
|
||||||
out.concat(buf);
|
#ifdef ESP8266
|
||||||
|
buffer.concat(PSTR(": "));
|
||||||
|
#else
|
||||||
|
buffer.concat(": ");
|
||||||
|
#endif
|
||||||
|
buffer.concat(header.value());
|
||||||
|
buffer.concat(T_rn);
|
||||||
}
|
}
|
||||||
_headers.clear();
|
_headers.clear();
|
||||||
|
|
||||||
out.concat(T_rn);
|
buffer.concat(T_rn);
|
||||||
_headLength = out.length();
|
_headLength = buffer.length();
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
|
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
|
||||||
@@ -337,7 +356,8 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const char* contentType, const
|
|||||||
|
|
||||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) {
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
String out = _assembleHead(request->version());
|
String out;
|
||||||
|
_assembleHead(out, request->version());
|
||||||
size_t outLen = out.length();
|
size_t outLen = out.length();
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
if (!_contentLength && space >= outLen) {
|
if (!_contentLength && space >= outLen) {
|
||||||
@@ -411,7 +431,7 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _c
|
|||||||
|
|
||||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) {
|
void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
addHeader(T_Connection, T_close, false);
|
addHeader(T_Connection, T_close, false);
|
||||||
_head = _assembleHead(request->version());
|
_assembleHead(_head, request->version());
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
_ack(request, 0, 0);
|
_ack(request, 0, 0);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user