Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] Threading model (was Re: Jetty 12 schedule?_

> 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 / joakim@xxxxxxxxxxx


On Wed, Nov 9, 2022 at 5:22 PM Cantor, Scott <cantor.2@xxxxxxx> 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@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/jetty-users

Back to the top