Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,13 +577,13 @@ request->multipart(); // bool: True if the request has content type "mult
int headers = request->headers();
int i;
for(i=0;i<headers;i++){
AsyncWebHeader* h = request->getHeader(i);
const AsyncWebHeader* h = request->getHeader(i);
Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}

//get specific header by name
if(request->hasHeader("MyHeader")){
AsyncWebHeader* h = request->getHeader("MyHeader");
const AsyncWebHeader* h = request->getHeader("MyHeader");
Serial.printf("MyHeader: %s\n", h->value().c_str());
}

Expand Down
69 changes: 69 additions & 0 deletions examples/Headers/Headers.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

//
// Query and send headers
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

static AsyncWebServer server(80);

void setup() {
Serial.begin(115200);

#ifndef CONFIG_IDF_TARGET_ESP32H2
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
#endif

//
// curl -v http://192.168.4.1
//
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
//List all collected headers
int headers = request->headers();
int i;
for (i = 0; i < headers; i++) {
const AsyncWebHeader *h = request->getHeader(i);
Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}

AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!");

//Add header to the response
response->addHeader("Server", "ESP Async Web Server");

//Add multiple headers with the same name
response->addHeader("Set-Cookie", "sessionId=38afes7a8", false);
response->addHeader("Set-Cookie", "id=a3fWa; Max-Age=2592000", false);
response->addHeader("Set-Cookie", "qwerty=219ffwef9w0f; Domain=example.com", false);

//Remove specific header
response->removeHeader("Set-Cookie", "sessionId=38afes7a8");

//Remove all headers with the same name
response->removeHeader("Set-Cookie");

request->send(response);
});

server.begin();
}

void loop() {
//Sleep in the loop task to not keep the CPU busy
delay(1000);
}
3 changes: 3 additions & 0 deletions src/ESPAsyncWebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,8 @@ class AsyncWebServerResponse {
size_t _writtenLength;
WebResponseState _state;

static bool headerMustBePresentOnce(const String &name);

public:
static const char *responseCodeToString(int code);

Expand All @@ -979,6 +981,7 @@ class AsyncWebServerResponse {
return addHeader(name.c_str(), value, replaceExisting);
}
bool removeHeader(const char *name);
bool removeHeader(const char *name, const char *value);
const AsyncWebHeader *getHeader(const char *name) const;
const std::list<AsyncWebHeader> &getHeaders() const {
return _headers;
Expand Down
28 changes: 26 additions & 2 deletions src/WebResponses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,21 @@ void AsyncWebServerResponse::setContentType(const char *type) {
}

bool AsyncWebServerResponse::removeHeader(const char *name) {
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
bool h_erased = false;
for (auto i = _headers.begin(); i != _headers.end();) {
if (i->name().equalsIgnoreCase(name)) {
_headers.erase(i);
h_erased = true;
} else {
++i;
}
}
return h_erased;
}

bool AsyncWebServerResponse::removeHeader(const char *name, const char *value) {
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
if (i->name().equalsIgnoreCase(name) && i->value().equalsIgnoreCase(value)) {
_headers.erase(i);
return true;
}
Expand All @@ -112,6 +125,15 @@ const AsyncWebHeader *AsyncWebServerResponse::getHeader(const char *name) const
return (iter == std::end(_headers)) ? nullptr : &(*iter);
}

bool AsyncWebServerResponse::headerMustBePresentOnce(const String &name) {
for (uint8_t i = 0; i < T_only_once_headers_len; i++) {
if (name.equalsIgnoreCase(T_only_once_headers[i])) {
return true;
}
}
return false;
}

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 @@ -120,9 +142,11 @@ bool AsyncWebServerResponse::addHeader(const char *name, const char *value, bool
// remove, break and add the new one
_headers.erase(i);
break;
} else {
} else if (headerMustBePresentOnce(i->name())) { // we can have only one header with that name
// do not update
return false;
} else {
break; // accept multiple headers with the same name
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/literals.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ static constexpr const char *T_Content_Disposition = "content-disposition";
static constexpr const char *T_Content_Encoding = "content-encoding";
static constexpr const char *T_Content_Length = "content-length";
static constexpr const char *T_Content_Type = "content-type";
static constexpr const char *T_Content_Location = "content-location";
static constexpr const char *T_Cookie = "cookie";
static constexpr const char *T_CORS_ACAC = "access-control-allow-credentials";
static constexpr const char *T_CORS_ACAH = "access-control-allow-headers";
Expand All @@ -36,6 +37,7 @@ static constexpr const char *T_CORS_ACAO = "access-control-allow-origin";
static constexpr const char *T_CORS_ACMA = "access-control-max-age";
static constexpr const char *T_CORS_O = "origin";
static constexpr const char *T_data_ = "data: ";
static constexpr const char *T_Date = "date";
static constexpr const char *T_DIGEST = "digest";
static constexpr const char *T_DIGEST_ = "digest ";
static constexpr const char *T_ETag = "etag";
Expand Down Expand Up @@ -71,6 +73,7 @@ static constexpr const char *T_retry_after = "retry-after";
static constexpr const char *T_nn = "\n\n";
static constexpr const char *T_rn = "\r\n";
static constexpr const char *T_rnrn = "\r\n\r\n";
static constexpr const char *T_Server = "server";
static constexpr const char *T_Transfer_Encoding = "transfer-encoding";
static constexpr const char *T_TRUE = "true";
static constexpr const char *T_UPGRADE = "upgrade";
Expand Down Expand Up @@ -183,4 +186,8 @@ static constexpr const char *T_HTTP_CODE_504 = "Gateway Time-out";
static constexpr const char *T_HTTP_CODE_505 = "HTTP Version not supported";
static constexpr const char *T_HTTP_CODE_ANY = "Unknown code";

static constexpr const uint8_t T_only_once_headers_len = 11;
static constexpr const char *T_only_once_headers[] = {T_Content_Length, T_Content_Type, T_Date, T_ETag, T_Last_Modified, T_LOCATION, T_retry_after,
T_Transfer_Encoding, T_Content_Location, T_Server, T_WWW_AUTH};

} // namespace asyncsrv