Skip to content

Commit e0a7b64

Browse files
refactor: replace bitmask enum with http_method from http_parser, list-based composite
- Remove AsyncWebRequestMethod namespace and bitmask enum - #include <http_parser.h> (with __has_include fallback) for http_method enum; HTTP_GET, HTTP_POST, etc. now come directly from the platform, eliminating the source of name conflicts with Arduino core / esp-idf headers - typedef http_method WebRequestMethod - WebRequestMethodComposite: fixed-size list-based class (empty = match any); operator| builds the list, operator^ checks membership - Remove ASYNCWEBSERVER_NO_GLOBAL_HTTP_METHODS and ASYNCWEBSERVER_NO_HTTP_ANY - HTTP_ALL = WebRequestMethodComposite() macro (always safe); HTTP_ANY = same but #ifndef-guarded (not defined if external macro exists) - AsyncWebServerRequest::_method changed to WebRequestMethod (single method) - method() return type changed to WebRequestMethod - Fix constexpr static bool JsonHandlerMethods bug in AsyncJson.cpp (fix-400) - All canHandle checks use operator^ instead of bitmask & Co-authored-by: mathieucarbou <61346+mathieucarbou@users.noreply.github.com>
1 parent 80f838d commit e0a7b64

6 files changed

Lines changed: 156 additions & 128 deletions

File tree

src/AsyncJson.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,21 @@ size_t AsyncMessagePackResponse::_fillBuffer(uint8_t *data, size_t len) {
112112
#endif
113113

114114
// Body handler supporting both content types: JSON and MessagePack
115-
constexpr static bool JsonHandlerMethods =
116-
AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH;
117115

118116
#if ARDUINOJSON_VERSION_MAJOR == 6
119117
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
120-
: _uri(std::move(uri)), _method(JsonHandlerTypes), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
118+
: _uri(std::move(uri)),
119+
_method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH),
120+
_onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
121121
#else
122122
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest)
123-
: _uri(std::move(uri)), _method(JsonHandlerMethods), _onRequest(onRequest), _maxContentLength(16384) {}
123+
: _uri(std::move(uri)),
124+
_method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH),
125+
_onRequest(onRequest), _maxContentLength(16384) {}
124126
#endif
125127

126128
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const {
127-
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
129+
if (!_onRequest || !request->isHTTP() || !(_method ^ request->method())) {
128130
return false;
129131
}
130132

@@ -133,17 +135,17 @@ bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) cons
133135
}
134136

135137
#if ASYNC_MSG_PACK_SUPPORT == 1
136-
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)
137139
|| request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack);
138140
#else
139-
return request->method() == AsyncWebRequestMethod::HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json);
141+
return request->method() == HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json);
140142
#endif
141143
}
142144

143145
void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request) {
144146
if (_onRequest) {
145147
// GET request:
146-
if (request->method() == AsyncWebRequestMethod::HTTP_GET) {
148+
if (request->method() == HTTP_GET) {
147149
JsonVariant json;
148150
_onRequest(request, json);
149151
return;

src/ESPAsyncWebServer.h

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

48+
// HTTP method enum from the underlying http_parser library.
49+
// All supported TCP libraries (AsyncTCP, ESPAsyncTCP, RPAsyncTCP) are built on
50+
// http_parser, so http_method is available on all supported platforms.
51+
// Using the platform's enum avoids redefining HTTP_GET, HTTP_POST, etc. ourselves,
52+
// which eliminates name conflicts with other libraries (e.g., Arduino WebServer.h).
53+
#if __has_include(<http_parser.h>)
54+
# include <http_parser.h>
55+
#else
56+
// Fallback definition matching the llhttp/http_parser spec (used by all platforms).
57+
// This is provided only for toolchains that do not expose http_parser.h directly.
58+
typedef enum {
59+
HTTP_DELETE = 0, HTTP_GET = 1, HTTP_HEAD = 2, HTTP_POST = 3, HTTP_PUT = 4,
60+
HTTP_CONNECT = 5, HTTP_OPTIONS = 6, HTTP_TRACE = 7, HTTP_COPY = 8, HTTP_LOCK = 9,
61+
HTTP_MKCOL = 10, HTTP_MOVE = 11, HTTP_PROPFIND = 12, HTTP_PROPPATCH = 13,
62+
HTTP_SEARCH = 14, HTTP_UNLOCK = 15, HTTP_BIND = 16, HTTP_REBIND = 17,
63+
HTTP_UNBIND = 18, HTTP_ACL = 19, HTTP_REPORT = 20, HTTP_MKACTIVITY = 21,
64+
HTTP_CHECKOUT = 22, HTTP_MERGE = 23, HTTP_MSEARCH = 24, HTTP_NOTIFY = 25,
65+
HTTP_SUBSCRIBE = 26, HTTP_UNSUBSCRIBE = 27, HTTP_PATCH = 28, HTTP_PURGE = 29,
66+
HTTP_MKCALENDAR = 30, HTTP_LINK = 31, HTTP_UNLINK = 32,
67+
} http_method;
68+
#endif
69+
4870
#include "AsyncWebServerVersion.h"
4971
#define ASYNCWEBSERVER_FORK_ESP32Async
5072

@@ -78,61 +100,94 @@ class AsyncCallbackWebHandler;
78100
class AsyncResponseStream;
79101
class AsyncMiddlewareChain;
80102

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_ALL = 0b0111111111111111,
101-
};
102-
}; // namespace AsyncWebRequestMethod
103+
// WebRequestMethod: a single HTTP request method, taken directly from the
104+
// platform's http_parser enum. HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT,
105+
// HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS, HTTP_PROPFIND, HTTP_LOCK, HTTP_UNLOCK,
106+
// HTTP_PROPPATCH, HTTP_MKCOL, HTTP_MOVE, HTTP_COPY and many others are already
107+
// defined globally by <http_parser.h> above — no redefinition needed here.
108+
typedef http_method WebRequestMethod;
109+
110+
// WebRequestMethodComposite: an ordered list of HTTP methods that a handler
111+
// accepts. An empty composite matches *any* method (HTTP_ANY / HTTP_ALL
112+
// semantics). Using a list avoids bitmask arithmetic on the platform enum's
113+
// non-power-of-two sequential values.
114+
class WebRequestMethodComposite {
115+
public:
116+
// Maximum number of methods that can be listed. In practice, handlers
117+
// accept at most a handful of methods; use the empty composite (HTTP_ALL)
118+
// when you want to match any method.
119+
static const uint8_t MAX_METHODS = 8;
103120

104-
typedef AsyncWebRequestMethod::AsyncWebRequestMethodType WebRequestMethod;
105-
typedef uint16_t WebRequestMethodComposite;
121+
// Empty composite = match any method.
122+
WebRequestMethodComposite() : _count(0) {}
106123

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;
113-
};
124+
// Single-method composite. Intentionally implicit so that a bare
125+
// WebRequestMethod (e.g. HTTP_GET) is accepted wherever a composite is
126+
// expected.
127+
WebRequestMethodComposite(WebRequestMethod m) : _count(1) {
128+
_methods[0] = m;
129+
}
114130

115-
// HTTP_ANY is commonly defined as a macro in Arduino core HTTP client libraries (e.g., ESP8266 HTTPClient.h).
116-
// Macros are expanded by the preprocessor before C++ namespace resolution, so even a namespace-qualified
117-
// reference like AsyncWebRequestMethod::HTTP_ANY would be incorrectly expanded to AsyncWebRequestMethod::0.
118-
// To avoid this conflict, HTTP_ANY is provided here as a C++ constexpr (not a macro), after undefining
119-
// any previously defined HTTP_ANY macro. Use HTTP_ALL as the canonical non-conflicting name.
120-
// To suppress this behavior entirely (keeping any pre-existing HTTP_ANY macro and not providing the alias),
121-
// define ASYNCWEBSERVER_NO_HTTP_ANY before including this header.
122-
#if !defined(ASYNCWEBSERVER_NO_HTTP_ANY)
123-
#ifdef HTTP_ANY
124-
#warning \
125-
"HTTP_ANY macro is already defined and will be undefined to avoid conflicts with ESPAsyncWebServer. To suppress this and keep the original macro, define ASYNCWEBSERVER_NO_HTTP_ANY and use AsyncWebRequestMethod::HTTP_ALL instead."
126-
#undef HTTP_ANY
127-
#endif
128-
namespace AsyncWebRequestMethod {
129-
static constexpr AsyncWebRequestMethodType HTTP_ANY = HTTP_ALL;
130-
} // namespace AsyncWebRequestMethod
131-
#endif
131+
// Append a method; silently ignores the call once MAX_METHODS is reached.
132+
WebRequestMethodComposite &add(WebRequestMethod m) {
133+
if (_count < MAX_METHODS) {
134+
_methods[_count++] = m;
135+
}
136+
return *this;
137+
}
138+
139+
// Returns true when this composite contains (or should match) the given
140+
// method. An empty composite matches any method.
141+
bool contains(WebRequestMethod m) const {
142+
if (_count == 0) return true;
143+
for (uint8_t i = 0; i < _count; i++) {
144+
if (_methods[i] == m) return true;
145+
}
146+
return false;
147+
}
148+
149+
// Equality: true when the composite holds exactly this one method.
150+
bool operator==(WebRequestMethod m) const {
151+
return _count == 1 && _methods[0] == m;
152+
}
153+
bool operator!=(WebRequestMethod m) const { return !(*this == m); }
154+
155+
// True when this is an empty (match-any) composite.
156+
bool empty() const { return _count == 0; }
157+
158+
private:
159+
WebRequestMethod _methods[MAX_METHODS];
160+
uint8_t _count;
161+
};
132162

133-
#if !defined(ASYNCWEBSERVER_NO_GLOBAL_HTTP_METHODS)
134-
// Import the method enum values to the global namespace
135-
using namespace AsyncWebRequestMethod;
163+
// Build a composite from two individual methods: HTTP_GET | HTTP_POST
164+
inline WebRequestMethodComposite operator|(WebRequestMethod l, WebRequestMethod r) {
165+
WebRequestMethodComposite c;
166+
c.add(l).add(r);
167+
return c;
168+
}
169+
170+
// Extend a composite with one more method: (HTTP_GET | HTTP_POST) | HTTP_PUT
171+
inline WebRequestMethodComposite operator|(WebRequestMethodComposite c, WebRequestMethod r) {
172+
c.add(r);
173+
return c;
174+
}
175+
176+
// Membership test: returns true when composite c contains method m.
177+
// Usage: if (handler._method ^ request->method()) { /* matched */ }
178+
inline bool operator^(const WebRequestMethodComposite &c, WebRequestMethod m) {
179+
return c.contains(m);
180+
}
181+
182+
// HTTP_ALL: empty composite — matches any HTTP method.
183+
// HTTP_ANY: backward-compatible alias for HTTP_ALL.
184+
// HTTP_ANY is only defined here when external headers have not already defined
185+
// it as a macro (e.g., Arduino core may define HTTP_ANY as an integer). When
186+
// an external macro is present the name remains an integer; user code should
187+
// migrate to HTTP_ALL which is always safe.
188+
#define HTTP_ALL WebRequestMethodComposite()
189+
#ifndef HTTP_ANY
190+
#define HTTP_ANY WebRequestMethodComposite()
136191
#endif
137192

138193
#ifndef HAVE_FS_FILE_OPEN_MODE
@@ -283,7 +338,7 @@ class AsyncWebServerRequest {
283338
uint8_t _parseState;
284339

285340
uint8_t _version;
286-
WebRequestMethodComposite _method;
341+
WebRequestMethod _method;
287342
String _url;
288343
String _host;
289344
String _contentType;
@@ -373,7 +428,7 @@ class AsyncWebServerRequest {
373428
uint8_t version() const {
374429
return _version;
375430
}
376-
WebRequestMethodComposite method() const {
431+
WebRequestMethod method() const {
377432
return _method;
378433
}
379434
const String &url() const {
@@ -401,10 +456,10 @@ class AsyncWebServerRequest {
401456
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED)
402457
const;
403458
bool isWebSocketUpgrade() const {
404-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_WS);
459+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS);
405460
}
406461
bool isSSE() const {
407-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
462+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
408463
}
409464
bool isHTTP() const {
410465
return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP);
@@ -1574,7 +1629,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
15741629
bool removeHandler(AsyncWebHandler *handler);
15751630

15761631
AsyncCallbackWebHandler &on(AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1577-
return on(std::move(uri), AsyncWebRequestMethod::HTTP_ALL, onRequest);
1632+
return on(std::move(uri), HTTP_ALL, onRequest);
15781633
}
15791634
AsyncCallbackWebHandler &on(
15801635
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_ALL), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
65+
AsyncCallbackWebHandler() : _uri(), _method(), _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: 2 additions & 2 deletions
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 {
@@ -300,7 +300,7 @@ void AsyncCallbackWebHandler::setUri(AsyncURIMatcher uri) {
300300
}
301301

302302
bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest *request) const {
303-
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
303+
if (!_onRequest || !request->isHTTP() || !(_method ^ request->method())) {
304304
return false;
305305
}
306306
return _uri.matches(request);

0 commit comments

Comments
 (0)