-
Notifications
You must be signed in to change notification settings - Fork 98
No object wrapper identity on dispatch #411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
On Mon, 26 Jul 2021 at 21:50, @markt-asf Mark Thomas [email protected] wrote:
I don't know. The big question is to what extent do users wrap objects If we go this route, do you anticipate a new API that would let users |
@markt-asf There are undoubtably some applications that use this "feature" that will be broken if we use it. But I believe (without any concrete evidence) that there are more broken applications using async that do no realize they are in a race when they access the path methods. We are likely to fix more applications than we break by removing this "feature". The "feature" was always a bad idea, encourages bad design, can be broken with simple utility filters, etc. Note that many applications that use this "feature" do so between filters and servlets, which we can continue to support it there. The problem only really comes when there is a RequestDispatcher.forward or async dispatch, that mutates the path methods and has different behaviours for methods like |
Consider the following asynchronous Servlet: @WebServlet(urlPatterns = {"/async/*"}, asyncSupported = true)
@RunAs("special")
public static class AsyncServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
AsyncContext async = request.startAsync();
PrintWriter out = response.getWriter();
async.start( () ->
{
response.setStatus(HttpServletResponse.SC_OK);
out.printf("path=%s special=%b%n",
request.getServletPath(),
request.isUserInRole("special"));
async.complete();
});
}
} If invoked via a RequestDispatcher.forward(...), then the result produced by this Servlet is a race: will the thread dispatched to execute the lambda execute before or after the thread returns from the This problem is the result of the start/complete lifecycle of asynchronous Servlets permitting/encouraging arbitrary threads to call the existing APIs that were not designed to be thread-safe. This issue is avoided if the request object passed to doGet is immutable and if it is the target of a forward, it will always act as that target. |
Fix jakartaee#411 No Object Wrapper Identity requirement for dispatch
I've created draft PR #413 to illustrate the changes I'm proposing. Specifically that we only remove Object Wrapper Indentify for dispatches that change the behaviour of request/response objects and that those behaviour changes persist even after the dispatch is complete. I acknowledge that is will break some applications, but if we are unable to remove such bad features as this, then there is no future for the servlet-api, as other APIs will out compete us on: performance, simplicity of usage; robust applications etc. If we can't remove such legacy kruft now, then when? Will we forever by an racy asynchronous API because of this rarely used bad feature? As another example of why Object Wrapper Identity is bad, consider the following evil Servlet, which when the target of an include can use the race described above to set a response header even when it is not allowed to. @WebServlet(urlPatterns = {"/evil/*"})
public static class EvilIncludedServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
async.start(() ->
{
while(true)
{
try
{
response.setHeader("Not", "Allowed");
break;
}
catch(Exception e)
{}
}
});
}
} |
The Servlet specification allows for wrappers to do more than just override existing methods! In 6.2.2, the specification says that:
So the example above could introduce new API to access the original user principal:
In order for targets to be able to use these new APIs then they must be able to downcast the passed request/response to the known wrapper type:
This downcast will only work if the wrapped object is passed through the container without any further wrapping, thus the specification requires “wrapper object identity”:
This “wrapper object identity” requirement means that the container is unable to itself wrap requests and responses as they are passed to filters and servlets. This restriction has, directly and indirectly, a huge impact on the complexity, efficiency, and correctness of Servlet container implementations, all for very dubious and redundant benefits:
Bad Software Components
In the example of ServletB above, it is a very bad software component as it cannot be invoked simply by respecting the signature of its methods. The caller must have a priori knowledge that the passed request will be downcast and any other caller will be met with a ClassCastException. This defeats the whole point of an API specification like Servlets, which is to define good software components that can be variously assembled according to their API contracts.
No Multiple Concerns
It is not possible for multiple concerns to wrap request/responses. If another filter applies its own wrappers, then the downcast will fail. The requirement for “wrapper object identity” requires the application developer to have total control over all aspects of the application, which can be difficult with discovered web fragments and ServletContainerInitializers.
Mutable Requests
By far the biggest impact of “wrapper object identity” is that it forces requests to be mutable! Since the container is not allowed to do its own wrapping within RequestDispatcher.forward(...) then the container must make the original request object mutable so that it changes the value returned from getServletPath() to reflect the target of the dispatch. It is this impact that has significant impacts on complexity, efficiency, and correctness:
Unnecessary
New APIs can be passed on objects set as request attribute values that will pass through multiple other wrappers, coexist with other new APIs in attributes and do not require the core request methods to have mutable returns.
Conclusion
The “wrapper object identity” requirement has little utility yet significant impacts on the correctness and performance of implementations. It significantly impairs the implementation of the container for a feature that can be rendered unusable by a wrapper applied by another filter. It should be removed from Servlet 6.0 and requests passed in by the container should be immutable.
The text was updated successfully, but these errors were encountered: