servlet
servlet copied to clipboard
Async Request parameters & Parts
Section 9.7.2 describes a set of request attributes that contain the path values of the original request, so that they may be accessed by a servlet called as a result of a AsyncContext.dispatch(...)
However, this implies that these attributes are only set after a AsyncContext.dispatch(...), which means that they are not available to a thread that might be acting as part of a startAsync().... AsyncContext.complete() pattern.
Note that a thread cannot access the original request paths via AsyncContext.getRequest().getServletPath() because the value returned from that can be affected by forwards that happen before and/or after the startAsync call, or even a forward after an async dispatch. The path methods are inherently volatile.
I think that the ASYNC request parameters should be set when startAsync is called, so that those values are available for the entire async life cycle and not only during async dispatch.
- Issue Imported From: https://github.com/javaee/servlet-spec/issues/31
- Original Issue Raised By:@glassfishrobot
- Original Issue Assigned To: @glassfishrobot
@glassfishrobot Commented Reported by gregwilkins
@glassfishrobot Commented gregwilkins said: Note also that the language used in this section could be improved, where it says:
The values of these attributes must be equal to the return values of the HttpServletRequest methods getRequestURI, getContextPath, getServletPath, getPathInfo, getQueryString respectively, invoked on the request object passed to the first servlet object in the call chain that received the request from the client.
A request might never be passed to a servlet as it might be handled entirely by filters, or the first servlet object might be changed by a filter doing a dispatch.
@glassfishrobot Commented rstoyanchev said:
can be affected by forwards that happen before and/or after the startAsync call
Greg, the impression I got from #41 is that forwards cannot happen after a call to startAsync or are you referring to a different scenario?
@glassfishrobot Commented gregwilkins said: very late response.
I don't see why a request can't be forwarded after async is started. It would be strange way to handle a request, but I see no reason for it to be prohibited.
But even if it can't be forwarded, it can be return from previous forwards after startAsync, so the values returned by the request.getXxx() methods will be volatile if access from an asynchronous thread.
Hence I believe the request attributes are the best way to obtain path information as they are immutable once set and thus thread safe.
@glassfishrobot Commented This issue was imported from java.net JIRA SERVLET_SPEC-31
See discussions:
- https://download.oracle.com/javaee-archive/servlet-spec.java.net/jsr369-experts/2015/12/0318.html
- https://www.eclipse.org/lists/servlet-dev/msg00103.html
Here are the proposals from the first thread:
/**
* <p>
* This class represents a call-back mechanism that will notify implementations
* as HTTP body parameters becomes available to be read without blocking.
* </p>
* <p>
* A typical usage pattern is:
* <pre>
* class MyParameterListener implements ParameterListener
* {
* public void onParameterAvailable(Stream stream) throws IOException
* {
* while(stream.isReady())
* process(stream.getParameter());
* }
* }
* </pre>
*/
public interface ParameterListener
{
/**
* When an instance of the <code>ParameterListener</code> is registered with a {@link ServletInputStream},
* this method will be invoked by the container the first time when it is possible
* to read a Parameter. Subsequently the container will invoke this method if and only
* if {@link Stream#isReady()} method has been called and has returned <code>false</code>.
*
* @throws IOException if an I/O related error has occurred during processing
*/
void onParameterAvailable(ParameterListener.Stream stream) throws IOException;
/**
* Invoked when all data for the current request has been read.
*
* @throws IOException if an I/O related error has occurred during processing
*/
public default void onAllDataRead() throws IOException {};
/**
* Invoked when an error occurs processing the request.
*/
public default void onError(Throwable t) {};
public interface Stream
{
/**
* @return True if a Parameter is available to be returned from getParameter();
*/
boolean isReady();
/**
* @return A Parameter that has been read from the request body. The Parameter will be fully
* constituted either in memory or as a file, so that any calls on the {@link Parameter#getInputStream()}
* will not block on the remote connection.
* @throws IllegalStateException if {@link #isReady()} has not been call or has been
* called and returned false.
*/
Map.Entry<String, String> getParameter();
}
}
/**
* <p>
* This class represents a call-back mechanism that will notify implementations
* as HTTP multipart request data becomes available to be read without blocking.
* </p>
* <p>
* A typical usage pattern is:
* <pre>
* class MyPartListener implements PartListener
* {
* public void onPartAvailable(Stream stream) throws IOException
* {
* while(stream.isReady())
* process(stream.getPart());
* }
* }
* </pre>
*/
public interface PartListener
{
/**
* When an instance of the <code>PartListener</code> is registered with a {@link ServletInputStream},
* this method will be invoked by the container the first time when it is possible
* to read a part. Subsequently the container will invoke this method if and only
* if {@link Stream#isReady()} method has been called and has returned <code>false</code>.
*
* @throws IOException if an I/O related error has occurred during processing
*/
void onPartAvailable(PartListener.Stream stream) throws IOException;
/**
* Invoked when all data for the current request has been read.
*
* @throws IOException if an I/O related error has occurred during processing
*/
public default void onAllDataRead() throws IOException {};
/**
* Invoked when an error occurs processing the request.
*/
public default void onError(Throwable t) {};
public interface Stream
{
/**
* @return True if a Part is available to be returned from getPart();
*/
boolean isReady();
/**
* @return A Part that has been read from the request body. The part will be fully
* constituted either in memory or as a file, so that any calls on the {@link Part#getInputStream()}
* will not block on the remote connection.
* @throws IllegalStateException if {@link #isReady()} has not been call or has been
* called and returned false.
*/
Part getPart();
}
}