Skip to content

Commit a6bc157

Browse files
committed
Pass along headers and Retry-After
1 parent 454a508 commit a6bc157

6 files changed

Lines changed: 88 additions & 41 deletions

File tree

include/http/HTTPClient.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,23 @@ namespace OpenShock::HTTP {
2727

2828
inline HTTPResponse Get(const char* url) {
2929
auto response = GetInternal(url);
30-
if (response.error != HTTPError::None) return response.error;
30+
if (response.error != HTTPError::None) return HTTP::HTTPResponse(response.error, response.retryAfterSeconds);
3131

32-
return HTTP::HTTPResponse(m_state, response.data.statusCode, response.data.contentLength);
32+
return HTTP::HTTPResponse(m_state, response.statusCode, response.contentLength, std::move(response.headers));
3333
}
3434
template<typename T>
3535
inline JsonResponse<T> GetJson(const char* url, JsonParserFn<T> jsonParser) {
3636
auto response = GetInternal(url);
37-
if (response.error != HTTPError::None) return response.error;
37+
if (response.error != HTTPError::None) return HTTP::JsonResponse<T>(response.error, response.retryAfterSeconds);
3838

39-
return HTTP::JsonResponse(m_state, jsonParser, response.data.statusCode, response.data.contentLength);
39+
return HTTP::JsonResponse(m_state, jsonParser, response.statusCode, response.contentLength, std::move(response.headers));
4040
}
4141

4242
inline esp_err_t Close() {
4343
return m_state->Close();
4444
}
4545
private:
46-
struct InternalResult {
47-
HTTPError error;
48-
HTTPClientState::StartRequestResult data;
49-
};
50-
InternalResult GetInternal(const char* url);
46+
HTTPClientState::StartRequestResult GetInternal(const char* url);
5147

5248
std::shared_ptr<HTTPClientState> m_state;
5349
};

include/http/HTTPClientState.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010

1111
#include <esp_http_client.h>
1212

13+
#include <map>
1314
#include <string>
1415
#include <string_view>
15-
#include <variant>
16-
#include <vector>
1716

1817
namespace OpenShock::HTTP {
1918
class HTTPClientState {
@@ -27,13 +26,21 @@ namespace OpenShock::HTTP {
2726
return esp_http_client_set_header(m_handle, key, value);
2827
}
2928

29+
struct HeaderEntry {
30+
std::string key;
31+
std::string value;
32+
};
33+
3034
struct [[nodiscard]] StartRequestResult {
31-
uint16_t statusCode;
32-
bool isChunked;
33-
uint32_t contentLength;
35+
HTTPError error{};
36+
uint32_t retryAfterSeconds{};
37+
uint16_t statusCode{};
38+
bool isChunked{};
39+
uint32_t contentLength{};
40+
std::map<std::string, std::string> headers{};
3441
};
3542

36-
std::variant<StartRequestResult, HTTPError> StartRequest(esp_http_client_method_t method, const char* url, int writeLen);
43+
StartRequestResult StartRequest(esp_http_client_method_t method, const char* url, int writeLen);
3744

3845
// High-throughput streaming logic
3946
ReadResult<uint32_t> ReadStreamImpl(DownloadCallback cb);
@@ -72,6 +79,7 @@ namespace OpenShock::HTTP {
7279

7380
esp_http_client_handle_t m_handle;
7481
bool m_reading;
75-
std::vector<std::pair<std::string, std::string>> m_headers;
82+
uint32_t m_retryAfterSeconds;
83+
std::map<std::string, std::string> m_headers;
7684
};
7785
} // namespace OpenShock::HTTP

include/http/HTTPResponse.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "http/ReadResult.h"
88

99
#include <cstdint>
10+
#include <map>
1011
#include <memory>
1112
#include <string>
1213

@@ -19,24 +20,38 @@ namespace OpenShock::HTTP {
1920

2021
friend class HTTPClient;
2122

22-
HTTPResponse(std::shared_ptr<HTTPClientState> state, uint16_t statusCode, uint32_t contentLength)
23+
HTTPResponse(std::shared_ptr<HTTPClientState> state, uint16_t statusCode, uint32_t contentLength, std::map<std::string, std::string> headers)
2324
: m_state(state)
2425
, m_error(HTTPError::None)
26+
, m_retryAfterSeconds(0)
2527
, m_statusCode(statusCode)
2628
, m_contentLength(contentLength)
29+
, m_headers(std::move(headers))
2730
{
2831
}
2932
public:
3033
HTTPResponse(HTTPError error)
3134
: m_state()
3235
, m_error(error)
36+
, m_retryAfterSeconds()
3337
, m_statusCode(0)
3438
, m_contentLength(0)
39+
, m_headers()
40+
{
41+
}
42+
HTTPResponse(HTTPError error, uint32_t retryAfterSeconds)
43+
: m_state()
44+
, m_error(error)
45+
, m_retryAfterSeconds(retryAfterSeconds)
46+
, m_statusCode(0)
47+
, m_contentLength(0)
48+
, m_headers()
3549
{
3650
}
3751

3852
inline bool Ok() const { return m_error == HTTPError::None && !m_state.expired(); }
3953
inline HTTPError Error() const { return m_error; }
54+
inline uint32_t RetryAfterSeconds() const { return m_retryAfterSeconds; }
4055
inline uint16_t StatusCode() const { return m_statusCode; }
4156
inline uint32_t ContentLength() const { return m_contentLength; }
4257

@@ -65,7 +80,9 @@ namespace OpenShock::HTTP {
6580
private:
6681
std::weak_ptr<HTTPClientState> m_state;
6782
HTTPError m_error;
83+
uint32_t m_retryAfterSeconds;
6884
uint16_t m_statusCode;
6985
uint32_t m_contentLength;
86+
std::map<std::string, std::string> m_headers;
7087
};
7188
} // namespace OpenShock::HTTP

include/http/JsonResponse.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#include "http/ReadResult.h"
88

99
#include <cstdint>
10+
#include <map>
1011
#include <memory>
12+
#include <string>
1113

1214
namespace OpenShock::HTTP {
1315
class HTTPClient;
@@ -19,26 +21,41 @@ namespace OpenShock::HTTP {
1921

2022
friend class HTTPClient;
2123

22-
JsonResponse(std::shared_ptr<HTTPClientState> state, JsonParserFn<T> jsonParser, uint16_t statusCode, uint32_t contentLength)
24+
JsonResponse(std::shared_ptr<HTTPClientState> state, JsonParserFn<T> jsonParser, uint16_t statusCode, uint32_t contentLength, std::map<std::string, std::string> headers)
2325
: m_state(state)
2426
, m_jsonParser(jsonParser)
2527
, m_error(HTTPError::None)
28+
, m_retryAfterSeconds(0)
2629
, m_statusCode(statusCode)
2730
, m_contentLength(contentLength)
31+
, m_headers(std::move(headers))
2832
{
2933
}
3034
public:
3135
JsonResponse(HTTPError error)
3236
: m_state()
3337
, m_jsonParser()
3438
, m_error(error)
39+
, m_retryAfterSeconds(0)
3540
, m_statusCode(0)
3641
, m_contentLength(0)
42+
, m_headers()
43+
{
44+
}
45+
JsonResponse(HTTPError error, uint32_t retryAfterSeconds)
46+
: m_state()
47+
, m_jsonParser()
48+
, m_error(error)
49+
, m_retryAfterSeconds(retryAfterSeconds)
50+
, m_statusCode(0)
51+
, m_contentLength(0)
52+
, m_headers()
3753
{
3854
}
3955

4056
inline bool Ok() const { return m_error == HTTPError::None && !m_state.expired(); }
4157
inline HTTPError Error() const { return m_error; }
58+
inline uint32_t RetryAfterSeconds() const { return m_retryAfterSeconds; }
4259
inline uint16_t StatusCode() const { return m_statusCode; }
4360
inline uint32_t ContentLength() const { return m_contentLength; }
4461

@@ -53,7 +70,9 @@ namespace OpenShock::HTTP {
5370
std::weak_ptr<HTTPClientState> m_state;
5471
JsonParserFn<T> m_jsonParser;
5572
HTTPError m_error;
73+
uint32_t m_retryAfterSeconds;
5674
uint16_t m_statusCode;
5775
uint32_t m_contentLength;
76+
std::map<std::string, std::string> m_headers;
5877
};
5978
} // namespace OpenShock::HTTP

src/http/HTTPClient.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,18 @@ const char* const TAG = "HTTPClient";
1111

1212
using namespace OpenShock;
1313

14-
HTTP::HTTPClient::InternalResult HTTP::HTTPClient::GetInternal(const char* url) {
14+
HTTP::HTTPClientState::StartRequestResult HTTP::HTTPClient::GetInternal(const char* url) {
1515
auto ratelimiter = HTTP::RateLimiters::GetRateLimiter(url);
1616
if (ratelimiter == nullptr) {
1717
OS_LOGW(TAG, "Invalid URL!");
18-
return {HTTPError::InvalidUrl, {}};
18+
return { .error = HTTPError::InvalidUrl };
1919
}
2020

2121
if (!ratelimiter->tryRequest()) {
2222
OS_LOGW(TAG, "Hit ratelimit, refusing to send request!");
23-
return {HTTPError::RateLimited, {}};
23+
return { .error = HTTPError::RateLimited };
2424
}
2525

2626

27-
auto result = m_state->StartRequest(HTTP_METHOD_GET, url, 0);
28-
if (auto error = std::get_if<HTTPError>(&result)) {
29-
return {*error, {}};
30-
}
31-
32-
return {HTTPError::None, std::get<HTTPClientState::StartRequestResult>(result)};
27+
return m_state->StartRequest(HTTP_METHOD_GET, url, 0);
3328
}

src/http/HTTPClientState.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ using namespace OpenShock;
1818
HTTP::HTTPClientState::HTTPClientState(uint32_t timeoutMs)
1919
: m_handle(nullptr)
2020
, m_reading(false)
21-
, m_headers(false)
21+
, m_retryAfterSeconds(0)
22+
, m_headers()
2223
{
2324
esp_http_client_config_t cfg;
2425
memset(&cfg, 0, sizeof(cfg));
@@ -44,34 +45,45 @@ HTTP::HTTPClientState::~HTTPClientState()
4445
}
4546
}
4647

47-
std::variant<HTTP::HTTPClientState::StartRequestResult, HTTP::HTTPError> HTTP::HTTPClientState::StartRequest(esp_http_client_method_t method, const char* url, int writeLen)
48+
HTTP::HTTPClientState::StartRequestResult HTTP::HTTPClientState::StartRequest(esp_http_client_method_t method, const char* url, int writeLen)
4849
{
4950
esp_err_t err;
5051

52+
m_headers.clear();
53+
5154
err = esp_http_client_set_url(m_handle, url);
52-
if (err != ESP_OK) return HTTPError::InvalidUrl;
55+
if (err != ESP_OK) return { .error = HTTPError::InvalidUrl };
5356

5457
err = esp_http_client_set_method(m_handle, method);
55-
if (err != ESP_OK) return HTTPError::InvalidHttpMethod;
58+
if (err != ESP_OK) return { .error = HTTPError::InvalidHttpMethod };
5659

5760
err = esp_http_client_open(m_handle, writeLen);
58-
if (err != ESP_OK) return HTTPError::NetworkError;
61+
if (err != ESP_OK) return { .error = HTTPError::NetworkError };
5962

6063
int contentLength = esp_http_client_fetch_headers(m_handle);
61-
if (contentLength == ESP_FAIL) return HTTPError::NetworkError;
64+
if (contentLength == ESP_FAIL) return { .error = HTTPError::NetworkError };
65+
66+
if (m_retryAfterSeconds > 0) {
67+
return { .error = HTTPError::RateLimited, .retryAfterSeconds = m_retryAfterSeconds };
68+
}
6269

6370
bool isChunked = false;
6471
if (contentLength == 0) {
6572
isChunked = esp_http_client_is_chunked_response(m_handle);
6673
}
6774

68-
int code = esp_http_client_get_status_code(m_handle);
69-
if (code < 0 || code > 599) {
70-
OS_LOGE(TAG, "Returned statusCode is invalid (%i)", code);
71-
return HTTPError::NetworkError;
75+
int statusCode = esp_http_client_get_status_code(m_handle);
76+
if (statusCode < 0 || statusCode > 599) {
77+
OS_LOGE(TAG, "Returned statusCode is invalid (%i)", statusCode);
78+
return { .error = HTTPError::NetworkError };
7279
}
7380

74-
return StartRequestResult {static_cast<uint16_t>(code), isChunked, static_cast<uint32_t>(contentLength)};
81+
return StartRequestResult {
82+
.statusCode = static_cast<uint16_t>(statusCode),
83+
.isChunked = isChunked,
84+
.contentLength = static_cast<uint32_t>(contentLength),
85+
.headers = std::move(m_headers)
86+
};
7587
}
7688

7789
HTTP::ReadResult<uint32_t> HTTP::HTTPClientState::ReadStreamImpl(DownloadCallback cb)
@@ -181,16 +193,16 @@ esp_err_t HTTP::HTTPClientState::EventHeaderHandler(std::string key, std::string
181193
OS_LOGI(TAG, "Got header_received event: %.*s - %.*s", key.length(), key.c_str(), key.length(), key.c_str());
182194

183195
if (key == "Retry-After") {
184-
uint32_t seconds;
196+
uint32_t seconds = 0;
185197
if (!Convert::ToUint32(value, seconds) || seconds <= 0) {
186198
seconds = 15;
187199
}
188200

189201
OS_LOGI(TAG, "Retry-After: %d seconds, applying delay to rate limiter", seconds);
190-
// TODO: Inform caller
202+
m_retryAfterSeconds = seconds;
191203
}
192204

193-
m_headers.emplace_back(std::move(key), std::move(value));
205+
m_headers[key] = std::move(value);
194206

195207
return ESP_OK;
196208
}

0 commit comments

Comments
 (0)