Skip to content

Commit 12a670f

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 12a670f

9 files changed

Lines changed: 272 additions & 127 deletions

File tree

examples/HTTPMethods/HTTPMethods.ino

Lines changed: 10 additions & 7 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);
@@ -37,14 +35,19 @@ void setup() {
3735
WiFi.softAP("esp-captive");
3836
#endif
3937

40-
// curl -v http://192.168.4.1/get-or-post
41-
// 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) {
38+
// curl -v http://192.168.4.1/get-or-post => Hello
39+
// curl -v -X POST -d "a=b" http://192.168.4.1/get-or-post => Hello
40+
// curl -v -X PUT -d "a=b" http://192.168.4.1/get-or-post => 404
41+
// curl -v -X PATCH -d "a=b" http://192.168.4.1/get-or-post => 404
42+
server.on("/get-or-post", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest *request) {
4343
request->send(200, "text/plain", "Hello");
4444
});
4545

46-
// curl -v http://192.168.4.1/any
47-
server.on("/any", WebRequestMethod::HTTP_ANY, [](AsyncWebServerRequest *request) {
46+
// curl -v http://192.168.4.1/any => Hello
47+
// curl -v -X POST -d "a=b" http://192.168.4.1/any => Hello
48+
// curl -v -X PUT -d "a=b" http://192.168.4.1/any => Hello
49+
// curl -v -X PATCH -d "a=b" http://192.168.4.1/any => Hello
50+
server.on("/any", HTTP_ANY, [](AsyncWebServerRequest *request) {
4851
request->send(200, "text/plain", "Hello");
4952
});
5053

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: 167 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,97 @@ 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 allows(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+
String toString() const {
199+
if (empty()) {
200+
return "<>";
201+
}
202+
String result = "<";
203+
for (size_t i = 0; i < _methods.size(); ++i) {
204+
if (i > 0) {
205+
result += ",";
206+
}
207+
result.concat(_methods[i]);
208+
}
209+
result.concat(">");
210+
return result;
211+
}
212+
213+
private:
214+
std::vector<WebRequestMethod> _methods;
113215
};
114216

115-
#if !defined(ASYNCWEBSERVER_NO_GLOBAL_HTTP_METHODS)
116-
// Import the method enum values to the global namespace
117-
using namespace AsyncWebRequestMethod;
118-
#endif
217+
// Build a composite from two individual methods: HTTP_GET | HTTP_POST
218+
inline WebRequestMethodComposite operator|(WebRequestMethod l, WebRequestMethod r) {
219+
WebRequestMethodComposite c;
220+
c.add(l).add(r);
221+
return c;
222+
}
223+
224+
// Extend a composite with one more method: (HTTP_GET | HTTP_POST) | HTTP_PUT
225+
inline WebRequestMethodComposite operator|(WebRequestMethodComposite c, WebRequestMethod r) {
226+
c.add(r);
227+
return c;
228+
}
229+
230+
// Membership test: returns true when composite c contains method m.
231+
// Usage: if (handler._method & request->method()) { /* matched */ }
232+
inline bool operator&(const WebRequestMethodComposite &c, WebRequestMethod m) {
233+
return c.allows(m);
234+
}
235+
inline bool operator&(WebRequestMethod m, const WebRequestMethodComposite &c) {
236+
return c.allows(m);
237+
}
119238

120239
#ifndef HAVE_FS_FILE_OPEN_MODE
121240
namespace fs {
@@ -188,7 +307,7 @@ class AsyncWebHeader {
188307
[[deprecated("Use AsyncWebHeader::parse(data) instead")]]
189308
#endif
190309
AsyncWebHeader(const String &data)
191-
: AsyncWebHeader(parse(data)){};
310+
: AsyncWebHeader(parse(data)) {};
192311

193312
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
194313
AsyncWebHeader &operator=(AsyncWebHeader &&other) = default;
@@ -265,7 +384,7 @@ class AsyncWebServerRequest {
265384
uint8_t _parseState;
266385

267386
uint8_t _version;
268-
WebRequestMethodComposite _method;
387+
WebRequestMethod _method;
269388
String _url;
270389
String _host;
271390
String _contentType;
@@ -355,7 +474,7 @@ class AsyncWebServerRequest {
355474
uint8_t version() const {
356475
return _version;
357476
}
358-
WebRequestMethodComposite method() const {
477+
WebRequestMethod method() const {
359478
return _method;
360479
}
361480
const String &url() const {
@@ -374,19 +493,21 @@ class AsyncWebServerRequest {
374493
return _isMultipart;
375494
}
376495

496+
const char *methodToString(WebRequestMethod method) const;
377497
const char *methodToString() const;
378498
const char *requestedConnTypeToString() const;
379499

380500
RequestedConnectionType requestedConnType() const {
381501
return _reqconntype;
382502
}
383-
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED)
384-
const;
503+
bool isExpectedRequestedConnType(
504+
RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED
505+
) const;
385506
bool isWebSocketUpgrade() const {
386-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_WS);
507+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS);
387508
}
388509
bool isSSE() const {
389-
return _method == AsyncWebRequestMethod::HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
510+
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
390511
}
391512
bool isHTTP() const {
392513
return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP);
@@ -574,7 +695,8 @@ class AsyncWebServerRequest {
574695
return beginResponse(code, contentType.c_str(), content, len, callback);
575696
}
576697
#ifndef ESP8266
577-
[[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
698+
[[deprecated(
699+
"Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
578700
)]]
579701
#endif
580702
AsyncWebServerResponse *beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
@@ -964,6 +1086,8 @@ class AsyncURIMatcher {
9641086
#endif
9651087

9661088
private:
1089+
friend class AsyncWebServer;
1090+
9671091
// fields
9681092
String _value;
9691093
union {
@@ -1556,7 +1680,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
15561680
bool removeHandler(AsyncWebHandler *handler);
15571681

15581682
AsyncCallbackWebHandler &on(AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1559-
return on(std::move(uri), AsyncWebRequestMethod::HTTP_ANY, onRequest);
1683+
return on(std::move(uri), HTTP_ANY, onRequest);
15601684
}
15611685
AsyncCallbackWebHandler &on(
15621686
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)