-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttpCommon.h
More file actions
133 lines (121 loc) · 4.51 KB
/
HttpCommon.h
File metadata and controls
133 lines (121 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#ifndef HTTP_COMMON_H
#define HTTP_COMMON_H
#include <Arduino.h>
// Feature flags (can be overridden before including library headers)
#ifndef ASYNC_HTTP_ENABLE_GZIP_DECODE
#define ASYNC_HTTP_ENABLE_GZIP_DECODE \
0 // 0 = no gzip inflation. 1 = enable transparent gzip response decoding (Content-Encoding: gzip).
#endif
// Security: allow enabling insecure TLS (skips CA validation) only when explicitly opted in at build time.
// Recommended alternatives: `setTlsCACert(...)` or `setTlsFingerprint(...)` (pinning).
#ifndef ASYNC_HTTP_ALLOW_INSECURE_TLS
#define ASYNC_HTTP_ALLOW_INSECURE_TLS 0
#endif
// Library version (single source of truth inside code). Keep in sync with library.json and library.properties.
#ifndef ESP_ASYNC_WEB_CLIENT_VERSION
#define ESP_ASYNC_WEB_CLIENT_VERSION "2.1.2"
#endif
struct HttpHeader {
String name;
String value;
HttpHeader() {}
HttpHeader(const String& n, const String& v) : name(n), value(v) {
name.toLowerCase();
}
};
struct AsyncHttpTLSConfig {
String caCert;
String clientCert;
String clientPrivateKey;
String fingerprint; // SHA-256 hex (colon optional)
bool insecure = false; // true => skip CA verification
uint32_t handshakeTimeoutMs = 12000; // fallback if not overridden
};
enum HttpClientError {
CONNECTION_FAILED = -1,
HEADER_PARSE_FAILED = -2,
CONNECTION_CLOSED = -3,
REQUEST_TIMEOUT = -4,
HTTPS_NOT_SUPPORTED = -5,
CHUNKED_DECODE_FAILED = -6,
CONNECT_TIMEOUT = -7,
BODY_STREAM_READ_FAILED = -8,
ABORTED = -9,
CONNECTION_CLOSED_MID_BODY = -10,
MAX_BODY_SIZE_EXCEEDED = -11,
TOO_MANY_REDIRECTS = -12,
HEADERS_TOO_LARGE = -13,
TLS_HANDSHAKE_FAILED = -14,
TLS_CERT_INVALID = -15,
TLS_FINGERPRINT_MISMATCH = -16,
TLS_HANDSHAKE_TIMEOUT = -17,
GZIP_DECODE_FAILED = -18
};
inline const char* httpClientErrorToString(HttpClientError error) {
switch (error) {
case CONNECTION_FAILED:
return "Failed to initiate connection";
case HEADER_PARSE_FAILED:
return "Failed to parse response headers";
case CONNECTION_CLOSED:
return "Connection closed before headers received";
case REQUEST_TIMEOUT:
return "Request timeout";
case HTTPS_NOT_SUPPORTED:
return "HTTPS transport unavailable";
case CHUNKED_DECODE_FAILED:
return "Failed to decode chunked body";
case CONNECT_TIMEOUT:
return "Connect timeout";
case BODY_STREAM_READ_FAILED:
return "Body stream read failed";
case ABORTED:
return "Aborted by user";
case CONNECTION_CLOSED_MID_BODY:
return "Connection closed mid-body";
case MAX_BODY_SIZE_EXCEEDED:
return "Body exceeds configured maximum";
case TOO_MANY_REDIRECTS:
return "Too many redirects";
case HEADERS_TOO_LARGE:
return "Response headers exceed configured maximum";
case TLS_HANDSHAKE_FAILED:
return "TLS handshake failed";
case TLS_CERT_INVALID:
return "TLS certificate validation failed";
case TLS_FINGERPRINT_MISMATCH:
return "TLS fingerprint mismatch";
case TLS_HANDSHAKE_TIMEOUT:
return "TLS handshake timeout";
case GZIP_DECODE_FAILED:
return "Failed to decode gzip body";
default:
return "Network error";
}
}
// Basic validation to prevent request-line / header injection when user-provided strings are used.
// This is intentionally strict: CR/LF and ASCII control characters are rejected.
inline bool isValidHttpHeaderName(const String& name) {
if (name.length() == 0)
return false;
for (size_t i = 0; i < name.length(); ++i) {
unsigned char c = static_cast<unsigned char>(name.charAt(i));
bool ok = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '!' || c == '#' ||
c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' ||
c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
if (!ok)
return false;
}
return true;
}
inline bool isValidHttpHeaderValue(const String& value) {
for (size_t i = 0; i < value.length(); ++i) {
unsigned char c = static_cast<unsigned char>(value.charAt(i));
if (c == '\r' || c == '\n' || c == 0x00)
return false;
if ((c < 0x20 && c != '\t') || c == 0x7F)
return false;
}
return true;
}
#endif // HTTP_COMMON_H