> Is it fair to assume that the only way a request could possibly cross
threads like that is if there's also an actually explicit entry point into
one's application logic by which the container is actually invoking a
standard interface that is defined to accept the
HttpServletRequest/Response?

The assumption that you'll be passed a HttpServletRequest and/or
HttpServletResponse to these actions isn't 100% true.

Take for example the Async I/O events.

Those are listeners.

https://javadoc.io/static/jakarta.servlet/jakarta.servlet-api/5.0.0/jakarta/servlet/ReadListener.html
https://javadoc.io/static/jakarta.servlet/jakarta.servlet-api/5.0.0/jakarta/servlet/WriteListener.html

You are not expected to be manipulating / accessing / referencing the
request and/or response at this point in the process.
That should have happened long before you even engaged these listeners.

There are similar things elsewhere in the spec.
Keep in mind that there is no provision in the Servlet spec for notifying
of the lifecycle of the HttpServletRequest or HttpServletResponse objects.

If a method existed to know when the original HttpServletRequest and
HttpServletResponse was / is / has been ended it's lifecycle (and
optionally been recycled by the container), you could possible write code
to hold onto the request/response objects safely (but realities of
concurrency programming means that this kind of event can only occur AFTER
it's done, which is often too late to turn things off)

> In other words, I take your point that people can build code that
leverages various advanced async/etc. features in the servlet spec, and we
don't necessarily know that's happening, but it has to *be* some explicit
invocation of a container technology that in turn is going to call back in
with the "proper" servlet request/response, right?

The proper way is to use the request / response objects as passed to you,
and not hold onto them.
This even includes objects like HttpSession and the ServletInputStream /
ServletOutputStream.

As a servlet endpoint, you are expected to get what you need from the
request (headers, body, etc), formulate a response (status code, headers,
etc) and then produce a body.
The async processing exists to delay / suspend actions to later, but that
also results in a container managed thread for the AsyncContext, one that
the container is aware of and knows the scope/lifecycle of.
The async I/O for requests expects that you've gathered what you need from
the request url line / headers / etc before you start.
The async I/O for responses expects that you've decided what you are going
to send, and are setting up to send the body of the response.

If you need to track things between dispatches of the same exchange, it's
usually a good idea to use the request attributes to hold onto objects for
the scope of that one request.

> So it's difficult to understand how we could possibly be passed in a
different instance than the original one the servlet dispatch received
since there's nowhere for it to occur.

Don't forget about normal redispatch behavior.
An example of this is if you use the RequestDispatcher, this will be a
wrapped or altered request/response objects depending on your mode of use
(include vs forward).
You can also have an Error being processed via a redispatch using the
DispatcherType.ERROR (which can appear to be a different request/response,
but in reality is a spec mandated cleanup of the request/response before
redispatch).
In both of those cases, a request attribute works great to track behavior
throughout the entire request lifecycle (even redispatch).

> Basically it feels like for this to break, something has to call an API
that also involves passing in a callback interface that itself has to
receive the request/response back.

As I showed above, not all listeners / interfaces have the request/response
params given to you, the API was created to respect the Filter Chain,
DispatcherTypes, and Request/Response Wrappers along with the normal HTTP
lifecycle (eg: you can't change response status code or headers if the
HttpServletResponse.isCommitted() is true)

Imagine if the APIs contained the request/response objects everywhere,
which ones do we give you if wrapping occurs? (eg: if a Filter wrapped the
request, and added the listener, then the Servlet wrapped again, and then
that listener needed to fire, do we give you the servlet wrapped request or
the filter wrapped request?)

Joakim Erdfelt / joa...@webtide.com


On Wed, Nov 9, 2022 at 5:22 PM Cantor, Scott <canto...@osu.edu> wrote:

>     >    It's a general anti-pattern to hold onto, use, reference a
>     > HttpServletRequest or HttpServletResponse object outside of the
>     > dispatch from the container.
>
> One more question about this I guess...
>
> Is it fair to assume that the only way a request could possibly cross
> threads like that is if there's also an actually explicit entry point into
> one's application logic by which the container is actually invoking a
> standard interface that is defined to accept the
> HttpServletRequest/Response?
>
> In other words, I take your point that people can build code that
> leverages various advanced async/etc. features in the servlet spec, and we
> don't necessarily know that's happening, but it has to *be* some explicit
> invocation of a container technology that in turn is going to call back in
> with the "proper" servlet request/response, right?
>
> Otherwise how could one ever know which instance of the request/response
> interfaces to act on?
>
> All we do is implement servlets (or make use of them) that accept a
> servlet dispatch call in and respond out. Nothing else in our design
> implements any servlet APIs that can accept a request/response in a
> different way.
>
> So it's difficult to understand how we could possibly be passed in a
> different instance than the original one the servlet dispatch received
> since there's nowhere for it to occur.
>
> That's why we've struggled to grasp what the risk is as this has come up
> at times.
>
> I fully appreciate that you're speaking from a container/generality
> perspective here and that in the general case what you're saying is
> obviously true. And yes, it's an anti-pattern because it makes assumptions
> that aren't generally true. I'm strongly suspecting they are, however,
> still true for us (and frankly for the vast majority of traditional apps
> that just implement Servlet).
>
> Basically it feels like for this to break, something has to call an API
> that also involves passing in a callback interface that itself has to
> receive the request/response back.
>
> -- Scott
>
>
> _______________________________________________
> jetty-users mailing list
> jetty-users@eclipse.org
> To unsubscribe from this list, visit
> https://www.eclipse.org/mailman/listinfo/jetty-users
>
_______________________________________________
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users

Reply via email to