Skip to content

Commit 69549c8

Browse files
committed
feat(http-client): add support for collecting all HTTP headers, close espressif#10802
1 parent 20c0dde commit 69549c8

File tree

4 files changed

+125
-30
lines changed

4 files changed

+125
-30
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include <Arduino.h>
2+
3+
#include <WiFi.h>
4+
#include <assert.h>
5+
#include <HTTPClient.h>
6+
7+
#define USE_SERIAL Serial
8+
9+
// Enable or disable collecting all headers
10+
#define COLLECT_ALL_HEADERS true
11+
12+
void setup() {
13+
14+
USE_SERIAL.begin(115200);
15+
16+
USE_SERIAL.println();
17+
USE_SERIAL.println();
18+
USE_SERIAL.println();
19+
20+
for (uint8_t t = 4; t > 0; t--) {
21+
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
22+
USE_SERIAL.flush();
23+
delay(1000);
24+
}
25+
26+
WiFi.begin("SSID", "PASSWORD");
27+
28+
while (WiFi.status() != WL_CONNECTED) {
29+
delay(500);
30+
USE_SERIAL.print(".");
31+
}
32+
USE_SERIAL.println();
33+
USE_SERIAL.println("Connected to WiFi: " + WiFi.SSID());
34+
}
35+
36+
void loop() {
37+
38+
HTTPClient http;
39+
40+
USE_SERIAL.print("[HTTP] Preparing HTTP request...\n");
41+
// This page will return the headers we want to test + some others
42+
http.begin("https://httpbingo.org/response-headers?x-custom-header=value:42");
43+
44+
#if COLLECT_ALL_HEADERS
45+
// Collect all headers
46+
http.collectAllHeaders();
47+
#else
48+
// Collect specific headers, only that one will be stored
49+
const char *headerKeys[] = {"x-custom-header"};
50+
const size_t headerKeysCount = sizeof(headerKeys) / sizeof(headerKeys[0]);
51+
http.collectHeaders(headerKeys, headerKeysCount);
52+
#endif
53+
54+
USE_SERIAL.print("[HTTP] Sending HTTP GET request...\n");
55+
// start connection and send HTTP header
56+
int httpCode = http.GET();
57+
58+
// httpCode will be negative on error
59+
if (httpCode > 0) {
60+
// HTTP header has been send and Server response header has been handled
61+
USE_SERIAL.printf("[HTTP] GET response code: %d\n", httpCode);
62+
63+
USE_SERIAL.println("[HTTP] Headers collected:");
64+
for (size_t i = 0; i < http.headers(); i++) {
65+
USE_SERIAL.printf("[HTTP] - '%s': '%s'\n", http.headerName(i).c_str(), http.header(i).c_str());
66+
}
67+
68+
USE_SERIAL.println("[HTTP] Has header 'x-custom-header'? " + String(http.hasHeader("x-custom-header")) + " (expected true)");
69+
USE_SERIAL.printf("[HTTP] x-custom-header: '%s' (expected 'value:42')\n", http.header("x-custom-header").c_str());
70+
USE_SERIAL.printf("[HTTP] non-existing-header: '%s' (expected empty string)\n", http.header("non-existing-header").c_str());
71+
72+
#if COLLECT_ALL_HEADERS
73+
// Server response with multiple headers, one of them is 'server'
74+
USE_SERIAL.println("[HTTP] Has header 'server'? " + String(http.hasHeader("server")) + " (expected true)");
75+
USE_SERIAL.printf("[HTTP] server: '%s'\n", http.header("server").c_str());
76+
#endif
77+
78+
} else {
79+
USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
80+
}
81+
82+
http.end();
83+
84+
Serial.println("[HTTP] end connection\n\n");
85+
delay(5000);
86+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"requires_any": [
3+
"CONFIG_SOC_WIFI_SUPPORTED=y",
4+
"CONFIG_ESP_WIFI_REMOTE_ENABLED=y"
5+
]
6+
}

libraries/HTTPClient/src/HTTPClient.cpp

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ HTTPClient::~HTTPClient() {
9090
if (_client) {
9191
_client->stop();
9292
}
93-
if (_currentHeaders) {
94-
delete[] _currentHeaders;
95-
}
9693
if (_tcpDeprecated) {
9794
_tcpDeprecated.reset(nullptr);
9895
}
@@ -564,9 +561,12 @@ int HTTPClient::sendRequest(const char *type, uint8_t *payload, size_t size) {
564561
bool redirect = false;
565562
uint16_t redirectCount = 0;
566563
do {
567-
// wipe out any existing headers from previous request
568-
for (size_t i = 0; i < _headerKeysCount; i++) {
569-
if (_currentHeaders[i].value.length() > 0) {
564+
// wipe out any existing headers from previous request, but preserve the keys if collecting specific headers
565+
if (_collectAllHeaders) {
566+
_currentHeaders.clear();
567+
} else {
568+
// Only clear values, keep the keys for specific header collection
569+
for (size_t i = 0; i < _currentHeaders.size(); ++i) {
570570
_currentHeaders[i].value.clear();
571571
}
572572
}
@@ -1015,19 +1015,24 @@ void HTTPClient::addHeader(const String &name, const String &value, bool first,
10151015
}
10161016
}
10171017

1018+
void HTTPClient::collectAllHeaders(bool collectAll) {
1019+
_collectAllHeaders = collectAll;
1020+
}
1021+
10181022
void HTTPClient::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) {
1019-
_headerKeysCount = headerKeysCount;
1020-
if (_currentHeaders) {
1021-
delete[] _currentHeaders;
1023+
if (_collectAllHeaders) {
1024+
log_w("collectHeaders is ignored when collectAllHeaders is set");
1025+
return;
10221026
}
1023-
_currentHeaders = new RequestArgument[_headerKeysCount];
1024-
for (size_t i = 0; i < _headerKeysCount; i++) {
1027+
_currentHeaders.clear();
1028+
_currentHeaders.resize(headerKeysCount);
1029+
for (size_t i = 0; i < headerKeysCount; i++) {
10251030
_currentHeaders[i].key = headerKeys[i];
10261031
}
10271032
}
10281033

10291034
String HTTPClient::header(const char *name) {
1030-
for (size_t i = 0; i < _headerKeysCount; ++i) {
1035+
for (size_t i = 0; i < _currentHeaders.size(); ++i) {
10311036
if (_currentHeaders[i].key.equalsIgnoreCase(name)) {
10321037
return _currentHeaders[i].value;
10331038
}
@@ -1036,25 +1041,25 @@ String HTTPClient::header(const char *name) {
10361041
}
10371042

10381043
String HTTPClient::header(size_t i) {
1039-
if (i < _headerKeysCount) {
1044+
if (i < _currentHeaders.size()) {
10401045
return _currentHeaders[i].value;
10411046
}
10421047
return String();
10431048
}
10441049

10451050
String HTTPClient::headerName(size_t i) {
1046-
if (i < _headerKeysCount) {
1051+
if (i < _currentHeaders.size()) {
10471052
return _currentHeaders[i].key;
10481053
}
10491054
return String();
10501055
}
10511056

10521057
int HTTPClient::headers() {
1053-
return _headerKeysCount;
1058+
return _currentHeaders.size();
10541059
}
10551060

10561061
bool HTTPClient::hasHeader(const char *name) {
1057-
for (size_t i = 0; i < _headerKeysCount; ++i) {
1062+
for (size_t i = 0; i < _currentHeaders.size(); ++i) {
10581063
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) {
10591064
return true;
10601065
}
@@ -1238,17 +1243,14 @@ int HTTPClient::handleHeaderResponse() {
12381243
setCookie(date, headerValue);
12391244
}
12401245

1241-
for (size_t i = 0; i < _headerKeysCount; i++) {
1242-
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
1243-
// Uncomment the following lines if you need to add support for multiple headers with the same key:
1244-
// if (!_currentHeaders[i].value.isEmpty()) {
1245-
// // Existing value, append this one with a comma
1246-
// _currentHeaders[i].value += ',';
1247-
// _currentHeaders[i].value += headerValue;
1248-
// } else {
1249-
_currentHeaders[i].value = headerValue;
1250-
// }
1251-
break; // We found a match, stop looking
1246+
if (_collectAllHeaders && headerName.length() > 0) {
1247+
_currentHeaders.emplace_back(headerName, headerValue);
1248+
} else {
1249+
for (size_t i = 0; i < _currentHeaders.size(); ++i) {
1250+
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
1251+
_currentHeaders[i].value = headerValue;
1252+
break; // We found a match, stop looking
1253+
}
12521254
}
12531255
}
12541256
}

libraries/HTTPClient/src/HTTPClient.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#include <NetworkClientSecure.h>
3939
#endif // HTTPCLIENT_NOSECURE
4040

41-
/// Cookie jar support
41+
/// Cookie jar and header support
4242
#include <vector>
4343

4444
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
@@ -238,6 +238,7 @@ class HTTPClient {
238238
void addHeader(const String &name, const String &value, bool first = false, bool replace = true);
239239

240240
/// Response handling
241+
void collectAllHeaders(bool collectAll = true);
241242
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount);
242243
String header(const char *name); // get request header value by name
243244
String header(size_t i); // get request header value by number
@@ -294,6 +295,7 @@ class HTTPClient {
294295
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
295296
bool _useHTTP10 = false;
296297
bool _secure = false;
298+
bool _collectAllHeaders = false;
297299

298300
String _uri;
299301
String _protocol;
@@ -304,8 +306,7 @@ class HTTPClient {
304306
String _acceptEncoding = "identity;q=1,chunked;q=0.1,*;q=0";
305307

306308
/// Response handling
307-
RequestArgument *_currentHeaders = nullptr;
308-
size_t _headerKeysCount = 0;
309+
std::vector<RequestArgument> _currentHeaders;
309310

310311
int _returnCode = 0;
311312
int _size = -1;

0 commit comments

Comments
 (0)