Skip to content

Commit f01d021

Browse files
Copilotmathieucarbou
authored andcommitted
Fix HTTP method conflicts with Arduino core: use HTTP_Method.h enum and list-based composite
1 parent ad3bafa commit f01d021

9 files changed

Lines changed: 242 additions & 126 deletions

File tree

examples/HTTPMethods/HTTPMethods.ino

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
#include <WiFi.h>
2424
#endif
2525

26-
#define ASYNCWEBSERVER_NO_GLOBAL_HTTP_METHODS 1
27-
#undef HTTP_ANY
2826
#include <ESPAsyncWebServer.h>
2927

3028
static AsyncWebServer server(80);
@@ -39,12 +37,12 @@ void setup() {
3937

4038
// curl -v http://192.168.4.1/get-or-post
4139
// curl -v -X POST -d "a=b" http://192.168.4.1/get-or-post
42-
server.on("/get-or-post", WebRequestMethod::HTTP_GET | WebRequestMethod::HTTP_POST, [](AsyncWebServerRequest *request) {
40+
server.on("/get-or-post", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest *request) {
4341
request->send(200, "text/plain", "Hello");
4442
});
4543

4644
// curl -v http://192.168.4.1/any
47-
server.on("/any", WebRequestMethod::HTTP_ANY, [](AsyncWebServerRequest *request) {
45+
server.on("/any", HTTP_ANY, [](AsyncWebServerRequest *request) {
4846
request->send(200, "text/plain", "Hello");
4947
});
5048

platformio.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ lib_dir = .
1515
; src_dir = examples/FlashResponse
1616
; src_dir = examples/HeaderManipulation
1717
; src_dir = examples/Headers
18-
; src_dir = examples/HTTPMethods
18+
src_dir = examples/HTTPMethods
1919
; src_dir = examples/Json
2020
; src_dir = examples/LargeResponse
2121
; src_dir = examples/Logging
2222
; src_dir = examples/MessagePack
2323
; src_dir = examples/Middleware
2424
; src_dir = examples/Params
2525
; src_dir = examples/PartitionDownloader
26-
src_dir = examples/PerfTests
26+
; src_dir = examples/PerfTests
2727
; src_dir = examples/RateLimit
2828
; src_dir = examples/Redirect
2929
; src_dir = examples/RequestContinuation

src/AsyncJson.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,11 @@ size_t AsyncMessagePackResponse::_fillBuffer(uint8_t *data, size_t len) {
115115

116116
#if ARDUINOJSON_VERSION_MAJOR == 6
117117
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
118-
: _uri(std::move(uri)),
119-
_method(AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH),
120-
_onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
118+
: _uri(std::move(uri)), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize),
119+
_maxContentLength(16384) {}
121120
#else
122121
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest)
123-
: _uri(std::move(uri)),
124-
_method(AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH),
125-
_onRequest(onRequest), _maxContentLength(16384) {}
122+
: _uri(std::move(uri)), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
126123
#endif
127124

128125
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const {
@@ -135,17 +132,17 @@ bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) cons
135132
}
136133

137134
#if ASYNC_MSG_PACK_SUPPORT == 1
138-
return request->method() == AsyncWebRequestMethod::HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)
135+
return request->method() == HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)
139136
|| request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack);
140137
#else
141-
return request->method() == AsyncWebRequestMethod::HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json);
138+
return request->method() == HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json);
142139
#endif
143140
}
144141

145142
void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request) {
146143
if (_onRequest) {
147144
// GET request:
148-
if (request->method() == AsyncWebRequestMethod::HTTP_GET) {
145+
if (request->method() == HTTP_GET) {
149146
JsonVariant json;
150147
_onRequest(request, json);
151148
return;

src/ESPAsyncWebServer.h

Lines changed: 149 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,72 @@
4545
#error Platform not supported
4646
#endif
4747

48+
// HTTP method types from the platform's HTTP parser library.
49+
// Arduino ESP32 core provides HTTP_Method.h which typedef's http_method as
50+
// HTTPMethod and defines HTTP_ANY = (HTTPMethod)(255) as the "match any" sentinel.
51+
// Fall back to the raw http_parser.h (always available via the TCP library) or,
52+
// as a last resort, to a fully inline fallback definition.
53+
#if __has_include(<HTTP_Method.h>)
54+
#include <HTTP_Method.h>
55+
// HTTP_Method.h provides: typedef enum http_method HTTPMethod;
56+
// and: #define HTTP_ANY (HTTPMethod)(255)
57+
#elif __has_include(<http_parser.h>)
58+
#include <http_parser.h>
59+
// http_parser.h provides enum http_method but not the HTTP_ANY sentinel.
60+
// Define the sentinel here, matching the value used by Arduino's HTTP_Method.h.
61+
#ifndef HTTP_ANY
62+
#define HTTP_ANY ((http_method)(255))
63+
#endif
64+
#else
65+
// Full fallback for toolchains that expose neither header.
66+
// Enum values match the llhttp/http_parser spec used by all supported platforms.
67+
typedef enum {
68+
HTTP_DELETE = 0,
69+
HTTP_GET = 1,
70+
HTTP_HEAD = 2,
71+
HTTP_POST = 3,
72+
HTTP_PUT = 4,
73+
/* pathological */
74+
HTTP_CONNECT = 5,
75+
HTTP_OPTIONS = 6,
76+
HTTP_TRACE = 7,
77+
/* WebDAV */
78+
HTTP_COPY = 8,
79+
HTTP_LOCK = 9,
80+
HTTP_MKCOL = 10,
81+
HTTP_MOVE = 11,
82+
HTTP_PROPFIND = 12,
83+
HTTP_PROPPATCH = 13,
84+
HTTP_SEARCH = 14,
85+
HTTP_UNLOCK = 15,
86+
HTTP_BIND = 16,
87+
HTTP_REBIND = 17,
88+
HTTP_UNBIND = 18,
89+
HTTP_ACL = 19,
90+
/* subversion */
91+
HTTP_REPORT = 20,
92+
HTTP_MKACTIVITY = 21,
93+
HTTP_CHECKOUT = 22,
94+
HTTP_MERGE = 23,
95+
/* upnp */
96+
HTTP_MSEARCH = 24,
97+
HTTP_NOTIFY = 25,
98+
HTTP_SUBSCRIBE = 26,
99+
HTTP_UNSUBSCRIBE = 27,
100+
/* RFC-5789 */
101+
HTTP_PATCH = 28,
102+
HTTP_PURGE = 29,
103+
/* CalDAV */
104+
HTTP_MKCALENDAR = 30,
105+
/* RFC-2068, section 19.6.1.2 */
106+
HTTP_LINK = 31,
107+
HTTP_UNLINK = 32,
108+
/* icecast */
109+
HTTP_SOURCE = 33,
110+
} http_method;
111+
#define HTTP_ANY ((http_method)(255))
112+
#endif
113+
48114
#include "AsyncWebServerVersion.h"
49115
#define ASYNCWEBSERVER_FORK_ESP32Async
50116

@@ -78,44 +144,82 @@ class AsyncCallbackWebHandler;
78144
class AsyncResponseStream;
79145
class AsyncMiddlewareChain;
80146

81-
// Namespace for web request method defines
82-
namespace AsyncWebRequestMethod {
83-
// The long name here is because we sometimes include this in the global namespace
84-
enum AsyncWebRequestMethodType {
85-
HTTP_GET = 0b0000000000000001,
86-
HTTP_POST = 0b0000000000000010,
87-
HTTP_DELETE = 0b0000000000000100,
88-
HTTP_PUT = 0b0000000000001000,
89-
HTTP_PATCH = 0b0000000000010000,
90-
HTTP_HEAD = 0b0000000000100000,
91-
HTTP_OPTIONS = 0b0000000001000000,
92-
HTTP_PROPFIND = 0b0000000010000000,
93-
HTTP_LOCK = 0b0000000100000000,
94-
HTTP_UNLOCK = 0b0000001000000000,
95-
HTTP_PROPPATCH = 0b0000010000000000,
96-
HTTP_MKCOL = 0b0000100000000000,
97-
HTTP_MOVE = 0b0001000000000000,
98-
HTTP_COPY = 0b0010000000000000,
99-
HTTP_RESERVED = 0b0100000000000000,
100-
HTTP_ANY = 0b0111111111111111,
101-
};
102-
}; // namespace AsyncWebRequestMethod
147+
// WebRequestMethod: a single HTTP request method, taken directly from the
148+
// platform's http_parser enum. HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT,
149+
// HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS, HTTP_PROPFIND, HTTP_LOCK, HTTP_UNLOCK,
150+
// HTTP_PROPPATCH, HTTP_MKCOL, HTTP_MOVE, HTTP_COPY and many others are already
151+
// defined globally by the platform headers above — no redefinition needed here.
152+
// HTTP_ANY (= 255) is the "match any method" sentinel, also from the platform.
153+
typedef http_method WebRequestMethod;
154+
155+
// WebRequestMethodComposite: an ordered list of HTTP methods that a handler
156+
// accepts. An empty composite matches *nothing*. A composite containing
157+
// HTTP_ANY matches *any* method.
158+
class WebRequestMethodComposite {
159+
public:
160+
// Empty composite = match nothing.
161+
WebRequestMethodComposite() {}
103162

104-
typedef AsyncWebRequestMethod::AsyncWebRequestMethodType WebRequestMethod;
105-
typedef uint16_t WebRequestMethodComposite;
163+
// Single-method composite.
164+
WebRequestMethodComposite(WebRequestMethod m) {
165+
_methods.push_back(m);
166+
}
106167

107-
// Type-safe helper functions for composite methods
108-
extern constexpr inline WebRequestMethodComposite operator|(WebRequestMethodComposite l, WebRequestMethod r) {
109-
return l | static_cast<WebRequestMethodComposite>(r);
110-
};
111-
extern constexpr inline WebRequestMethodComposite operator|(WebRequestMethod l, WebRequestMethod r) {
112-
return static_cast<WebRequestMethodComposite>(l) | r;
168+
// Append a method.
169+
WebRequestMethodComposite &add(WebRequestMethod m) {
170+
_methods.push_back(m);
171+
return *this;
172+
}
173+
174+
// Returns true when this composite contains (or should match) the given
175+
// method.
176+
bool contains(WebRequestMethod m) const {
177+
for (const auto &method : _methods) {
178+
if (method == HTTP_ANY || method == m) {
179+
return true;
180+
}
181+
}
182+
return false;
183+
}
184+
185+
// Equality: true when the composite holds exactly this one method.
186+
bool operator==(WebRequestMethod m) const {
187+
return _methods.size() == 1 && _methods[0] == m;
188+
}
189+
bool operator!=(WebRequestMethod m) const {
190+
return !(*this == m);
191+
}
192+
193+
// True when this is an empty (match-nothing) composite.
194+
bool empty() const {
195+
return _methods.empty();
196+
}
197+
198+
private:
199+
std::vector<WebRequestMethod> _methods;
113200
};
114201

115-
#if !defined(ASYNCWEBSERVER_NO_GLOBAL_HTTP_METHODS)
116-
// Import the method enum values to the global namespace
117-
using namespace AsyncWebRequestMethod;
118-
#endif
202+
// Build a composite from two individual methods: HTTP_GET | HTTP_POST
203+
inline WebRequestMethodComposite operator|(WebRequestMethod l, WebRequestMethod r) {
204+
WebRequestMethodComposite c;
205+
c.add(l).add(r);
206+
return c;
207+
}
208+
209+
// Extend a composite with one more method: (HTTP_GET | HTTP_POST) | HTTP_PUT
210+
inline WebRequestMethodComposite operator|(WebRequestMethodComposite c, WebRequestMethod r) {
211+
c.add(r);
212+
return c;
213+
}
214+
215+
// Membership test: returns true when composite c contains method m.
216+
// Usage: if (handler._method ^ request->method()) { /* matched */ }
217+
inline bool operator&(const WebRequestMethodComposite &c, WebRequestMethod m) {
218+
return c.contains(m);
219+
}
220+
inline bool operator&(WebRequestMethod m, const WebRequestMethodComposite &c) {
221+
return c.contains(m);
222+
}
119223

120224
#ifndef HAVE_FS_FILE_OPEN_MODE
121225
namespace fs {
@@ -188,7 +292,7 @@ class AsyncWebHeader {
188292
[[deprecated("Use AsyncWebHeader::parse(data) instead")]]
189293
#endif
190294
AsyncWebHeader(const String &data)
191-
: AsyncWebHeader(parse(data)){};
295+
: AsyncWebHeader(parse(data)) {};
192296

193297
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
194298
AsyncWebHeader &operator=(AsyncWebHeader &&other) = default;
@@ -265,7 +369,7 @@ class AsyncWebServerRequest {
265369
uint8_t _parseState;
266370

267371
uint8_t _version;
268-
WebRequestMethodComposite _method;
372+
WebRequestMethod _method;
269373
String _url;
270374
String _host;
271375
String _contentType;
@@ -355,7 +459,7 @@ class AsyncWebServerRequest {
355459
uint8_t version() const {
356460
return _version;
357461
}
358-
WebRequestMethodComposite method() const {
462+
WebRequestMethod method() const {
359463
return _method;
360464
}
361465
const String &url() const {
@@ -380,13 +484,14 @@ class AsyncWebServerRequest {
380484
RequestedConnectionType requestedConnType() const {
381485
return _reqconntype;
382486
}
383-
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED)
384-
const;
487+
bool isExpectedRequestedConnType(
488+
RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED
489+
) const;
385490
bool isWebSocketUpgrade() const {
386-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_WS);
491+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS);
387492
}
388493
bool isSSE() const {
389-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
494+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
390495
}
391496
bool isHTTP() const {
392497
return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP);
@@ -574,7 +679,8 @@ class AsyncWebServerRequest {
574679
return beginResponse(code, contentType.c_str(), content, len, callback);
575680
}
576681
#ifndef ESP8266
577-
[[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
682+
[[deprecated(
683+
"Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
578684
)]]
579685
#endif
580686
AsyncWebServerResponse *beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
@@ -1556,7 +1662,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
15561662
bool removeHandler(AsyncWebHandler *handler);
15571663

15581664
AsyncCallbackWebHandler &on(AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1559-
return on(std::move(uri), AsyncWebRequestMethod::HTTP_ANY, onRequest);
1665+
return on(std::move(uri), HTTP_ANY, onRequest);
15601666
}
15611667
AsyncCallbackWebHandler &on(
15621668
AsyncURIMatcher uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr,

src/Middleware.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ void AsyncCorsMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext n
249249
// Origin header ? => CORS handling
250250
if (request->hasHeader(asyncsrv::T_CORS_O)) {
251251
// check if this is a preflight request => handle it and return
252-
if (request->method() == AsyncWebRequestMethod::HTTP_OPTIONS) {
252+
if (request->method() == HTTP_OPTIONS) {
253253
AsyncWebServerResponse *response = request->beginResponse(200);
254254
addCORSHeaders(request, response);
255255
request->send(response);

src/WebHandlerImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AsyncCallbackWebHandler : public AsyncWebHandler {
6262
bool _isRegex;
6363

6464
public:
65-
AsyncCallbackWebHandler() : _uri(), _method(AsyncWebRequestMethod::HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
65+
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
6666
void setUri(AsyncURIMatcher uri);
6767
void setMethod(WebRequestMethodComposite method) {
6868
_method = method;

src/WebHandlers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified() {
103103
}
104104

105105
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) const {
106-
return request->isHTTP() && request->method() == AsyncWebRequestMethod::HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
106+
return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
107107
}
108108

109109
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) const {

0 commit comments

Comments
 (0)