mirror of
https://github.com/me-no-dev/ESPAsyncWebServer.git
synced 2025-09-28 15:21:03 +02:00
Merge pull request #88 from mathieucarbou/issue-86
Fix header handling versus internal handling of content-type and content-length
This commit is contained in:
@@ -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 {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -126,8 +126,7 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
|
||||
}
|
||||
}
|
||||
#else // ESP8266
|
||||
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code)
|
||||
{
|
||||
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100:
|
||||
return FPSTR(T_HTTP_CODE_100);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
@@ -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));
|
||||
if (_sendContentLength)
|
||||
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
|
||||
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, String(responseCodeToString(_code)).c_str());
|
||||
buffer.concat("HTTP/1.");
|
||||
#endif
|
||||
out.concat(buf);
|
||||
|
||||
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);
|
||||
}
|
||||
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; }
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user