Add support for Middleware - #98
Conversation
f174f7d to
a9b27da
Compare
|
@mathieucarbou This looks incredible—elegant and simple, while addressing all the original points! 👍🏻 Now, authentication can be checked in one place instead of several different locations, for WS/SSE and regular routes as well. Since #97 is locked for discussions, I'll provide my feedback here as requested. Does utilizing One quick question:
This is fine, as intercepting requests is often more useful than modifying responses, when designing for embedded applications. Response modifications can typically be handled by the Out of curiosity, though, why is this a limitation? I assume we would need to use some sort of buffer if we wanted to modify the response, to keep the original response. The current implementation supports both "streamed" responses as well as responses where the entire content is already kept in memory. Could a response middleware have been feasible for the latter type of response? Lastly, could you please add an example file once the PR is complete? The introduction of middlewares in ESPAsyncWebServer is surely incredibly useful, and new users would likely appreciate a quick example to implement in their own projects. Thank you for adding this! I'll switch to the |
|
So, it appears that the |
Yes, this is still WIP ;-) I just wanted to share the current state. |
This is the goal ;-)
Yes, and
But for the handlers, rewrites and middlewares, they are usually setup once, not often removed, and there are not a lot of elements. So a vector should be fine. I thought about using a pointer too (since this is merely a forward list), but it greatly increase the code size, so the benefit is lost.
No: handlers are like middleware and like filters: stateless objects (1 exception though) setup once, and all the requests go through them. There is no 1 middleware per request. So they have to stay in the handler. Otherwise, for next request, how the handler knows which middleware to execute ?
This is because in ESpAsyncWS, response creation is the responsibility of the handler, not the library. So the response object does not exist yet when going through the middleware, and even if a middleware calls This is still a valid use case, for example you could have a sort of logging middleware that would do:
Of course! Once it will be done I will add examples. |
a9b27da to
e8b328e
Compare
|
@DRSDavidSoft : PR changing std::list to std::vector for rewrites and handlers is here: #99 |
e8b328e to
0426b92
Compare
My knowledge of C++ is rusty here, so I would appreciate to understand how
You're right! I don't know why I assumed otherwise, I blame it on the lack of morning coffee 😅 If the middlewares are freed, then the subsequent requests wouldn't invoke them. Same for the handlers and rewrites.
Also curious about this!
That is correct, although it makes me think, what would happen if the Handler doesn't send a response? Never tested it out, curious what would happen!
Yeap, it is still a valid use case. The only limitation is that we wouldn't be able to interact with the response object being sent. I have some other questions as well:
Is there some reason to deprecate calls like this only for all non-ESP8266 cores? Additionally it can still be kept as an alias to middlewares and not be deprecated at all. This is just a suggestion, personally I prefer deprecating this for new projects, just a thought. One thing I forgot to ask: would it be possible to attach some sort of extra data to the request and have it be destroyed at the end of the request? This is the Maybe one middleware would create an object, populate it with some data, and attach it to the request (or even the other way around, the request would return a pointer or some sort of id that would be saved in that object) So that, later in the lifetime of the request, we would be able to retrieve the object containing the additional data (that was created during a middleware) and access it data. The freeing up part can theoretically be done using another middleware at the end. |
e1db8d2 to
3974b9b
Compare
There are plenty of writings for that on Internet. These are both lists but implemented with a different structure behind: vector with an array, and list with pointers to next and previous element, so each item holds 2 points on top. An array usually also requires to shift elements and resize after removal, which is a costly operation that can hardly be done concurrently. When the size is unknown, an array will also require a reallocation, which is costly.
this is linked to the tmp object attached in a post body or file upload.
this does not change from the current behaviour. I did not check.
ESP8266 does not compile with the
It has to be deprecated otherwise an
What you are looking for is called request attributes / request session (depending on the scope). It is not yet supported in ESPAsyncWS but I can easily add it in this PR.
this is exactly how it works usually, yes. you have an auth middleware that is checking auth, ans set the userId in the request object which is then available for other middleware and handlers cleanup can be done in the same middleware. example: addMiddleware([fn](AsyncWebServerRequest* request, ArMiddlewareNext next) {
// check auth
/// userId = ...
request.setAttribute("userId", userId);
next(); // further middleware and handler can call String userid = request.getAttribute("userId");
request.removeAttribute("userId");
});free is optional except if you have to use |
Got it. So it is to avoid creating duplicate middlewares. 👍🏻
This also is useful addition, too!
Please do! It would be super useful to be able to set and get attributes from different middlewares as well as the request itself, and would make the middleware support at least 10x more useful! 😄
Wow, this is exactly what I'm looking for! How about making the example file into some sort of complete authentication/authorization handler based on this, I'd like to contribute some code around the authentication logic, if I could.
That is awesome! The example you provided is so clean and concise, this is for sure the best implementation of middlewares for ESPAsyncWebserver! Also, using something like this I believe it would be possible to automatically free up any attached attributes at the end of the request's lifecycle. The great thing about this implementation of request attributes could allow the user to attach a pointer to any kind of data structure for later use! Although, in this case the data needs to be freed manually, I assume. These are all looking great and extremely useful for application development, wonder why something so useful as middleware support wasn't asked to be added to the ESPAsyncWebServer years ago... This all makes me excited for PsychicHTTP as well, I'm sure it'd be even more amazing than ESPAsyncWS 😄 |
f657210 to
deb0f83
Compare
|
@DRSDavidSoft : FYI #100 (request attributes) |
deb0f83 to
510da8d
Compare
274b992 to
0bf4831
Compare
8ff750c to
caae422
Compare
322e456 to
ea34ba1
Compare
fc0f8ac to
cec1fa6
Compare
|
@DRSDavidSoft : the PR is now complete. You can start testing in your app. I will do the same and I will merge in main once testing is done. You can look at the README in this PR and the SimpleServer sample for doc and use cases. Finally, this is a complete implementation of middleware: we can act on the request but also on the response, and even replacing it (i.e. rate limit middleware)
|
cec1fa6 to
f80581c
Compare
b9ee913 to
e9264d7
Compare
e9264d7 to
e6aea01
Compare
|
Hello @DRSDavidSoft , @vortigont : same goes for you ;-) All the latest changes are mostly additive (except a few things). |
This is a first draft of middleware implementation in ESPAsyncWebServer (a.k.a. Espressif Middleware).
Benefits:
AuthenticationMiddlewareandAuthorizationMiddlewareDrawback:
std::liston each handler (more memory used upfront per handler)Limitations:
Backward compatibility:
AsyncEventSourcewhere you must be sure to callsetAuthenticationBEFOREauthorizeConnect(bot deprecated) if you want to keep the same ordering as before, which is to check for authc before checking authz.Deprecations
Some methods are now deprecated in favour of middleware usage:
setAuthenticationauthorizeConnect