On Sun, Apr 7, 2019 at 10:42 PM <ma...@apache.org> wrote: > This is an automated email from the ASF dual-hosted git repository. > > markt pushed a commit to branch master > in repository https://gitbox.apache.org/repos/asf/tomcat.git > > > The following commit(s) were added to refs/heads/master by this push: > new 6d3c117 Fix the HTTP/2 equivalent of swallowInput > 6d3c117 is described below > > commit 6d3c117384c11f1bfd9393fb1484cd5a708a8245 > Author: Mark Thomas <ma...@apache.org> > AuthorDate: Sun Apr 7 21:39:15 2019 +0100 > > Fix the HTTP/2 equivalent of swallowInput > > When Tomcat writes a final response without reading all of an HTTP/2 > request, reset the stream to inform the client that the remaining > request body is not required. >
After this change, I do get this CI failure "reliably": https://ci.apache.org/projects/tomcat/tomcat9/logs/4183/TEST-org.apache.coyote.http2.TestHttp2Section_6_8.NIO2.txt Unsure if this is relatively normal or a bug. Rémy > --- > java/org/apache/coyote/AbstractProcessor.java | 7 +++++-- > java/org/apache/coyote/AbstractProcessorLight.java | 10 +++++++-- > .../apache/coyote/http2/LocalStrings.properties | 1 + > java/org/apache/coyote/http2/Stream.java | 2 +- > java/org/apache/coyote/http2/StreamProcessor.java | 24 > +++++++++++++++++++++- > webapps/docs/changelog.xml | 5 +++++ > 6 files changed, 43 insertions(+), 6 deletions(-) > > diff --git a/java/org/apache/coyote/AbstractProcessor.java > b/java/org/apache/coyote/AbstractProcessor.java > index 205a789..e7909ba 100644 > --- a/java/org/apache/coyote/AbstractProcessor.java > +++ b/java/org/apache/coyote/AbstractProcessor.java > @@ -197,7 +197,7 @@ public abstract class AbstractProcessor extends > AbstractProcessorLight implement > > > @Override > - public final SocketState dispatch(SocketEvent status) { > + public final SocketState dispatch(SocketEvent status) throws > IOException { > > if (status == SocketEvent.OPEN_WRITE && > response.getWriteListener() != null) { > asyncStateMachine.asyncOperation(); > @@ -950,6 +950,9 @@ public abstract class AbstractProcessor extends > AbstractProcessorLight implement > * > * @return The state to return for the socket once the clean-up for > the > * current request has completed > + * > + * @throws IOException If an I/O error occurs while attempting to end > the > + * request > */ > - protected abstract SocketState dispatchEndRequest(); > + protected abstract SocketState dispatchEndRequest() throws > IOException; > } > diff --git a/java/org/apache/coyote/AbstractProcessorLight.java > b/java/org/apache/coyote/AbstractProcessorLight.java > index 340c986..7a46c79 100644 > --- a/java/org/apache/coyote/AbstractProcessorLight.java > +++ b/java/org/apache/coyote/AbstractProcessorLight.java > @@ -152,10 +152,16 @@ public abstract class AbstractProcessorLight > implements Processor { > * Uses currently include Servlet 3.0 Async and HTTP upgrade > connections. > * Further uses may be added in the future. These will typically > start as > * HTTP requests. > + * > * @param status The event to process > - * @return the socket state > + * > + * @return The state the caller should put the socket in when this > method > + * returns > + * > + * @throws IOException If an I/O error occurs during the processing > of the > + * request > */ > - protected abstract SocketState dispatch(SocketEvent status); > + protected abstract SocketState dispatch(SocketEvent status) throws > IOException; > > protected abstract SocketState asyncPostProcess(); > > diff --git a/java/org/apache/coyote/http2/LocalStrings.properties > b/java/org/apache/coyote/http2/LocalStrings.properties > index c948c29..1320e60 100644 > --- a/java/org/apache/coyote/http2/LocalStrings.properties > +++ b/java/org/apache/coyote/http2/LocalStrings.properties > @@ -103,6 +103,7 @@ stream.reset.send=Connection [{0}], Stream [{1}], > Reset sent due to [{2}] > stream.trailerHeader.noEndOfStream=Connection [{0}], Stream [{1}], The > trailer headers did not include the end of stream flag > stream.writeTimeout=Timeout waiting for client to increase flow control > window to permit stream data to be written > > +streamProcessor.cancel=Connection [{0}], Stream [{1}], The remaining > request body is not required. > streamProcessor.error.connection=Connection [{0}], Stream [{1}], An error > occurred during processing that was fatal to the connection > streamProcessor.error.stream=Connection [{0}], Stream [{1}], An error > occurred during processing that was fatal to the stream > streamProcessor.flushBufferedWrite.entry=Connection [{0}], Stream [{1}], > Flushing buffered writes > diff --git a/java/org/apache/coyote/http2/Stream.java > b/java/org/apache/coyote/http2/Stream.java > index 3e64329..7337eb9 100644 > --- a/java/org/apache/coyote/http2/Stream.java > +++ b/java/org/apache/coyote/http2/Stream.java > @@ -676,7 +676,7 @@ class Stream extends AbstractStream implements > HeaderEmitter { > } > > > - private final boolean isInputFinished() { > + final boolean isInputFinished() { > return !state.isFrameTypePermitted(FrameType.DATA); > } > > diff --git a/java/org/apache/coyote/http2/StreamProcessor.java > b/java/org/apache/coyote/http2/StreamProcessor.java > index 6870926..90bf224 100644 > --- a/java/org/apache/coyote/http2/StreamProcessor.java > +++ b/java/org/apache/coyote/http2/StreamProcessor.java > @@ -361,6 +361,13 @@ class StreamProcessor extends AbstractProcessor { > setErrorState(ErrorState.CLOSE_NOW, e); > } > > + if (!isAsync()) { > + // If this is an async request then the request ends when it > has > + // been completed. The AsyncContext is responsible for calling > + // endRequest() in that case. > + endRequest(); > + } > + > if (sendfileState == SendfileState.PENDING) { > return SocketState.SENDFILE; > } else if (getErrorState().isError()) { > @@ -402,7 +409,22 @@ class StreamProcessor extends AbstractProcessor { > > > @Override > - protected final SocketState dispatchEndRequest() { > + protected final SocketState dispatchEndRequest() throws IOException { > + endRequest(); > return SocketState.CLOSED; > } > + > + > + private void endRequest() throws IOException { > + if (!stream.isInputFinished()) { > + // The request has been processed but the request body has > not been > + // fully read. This typically occurs when Tomcat rejects an > upload > + // of some form (e.g. PUT or POST). Need to tell the client > not to > + // send any more data. > + StreamException se = new StreamException( > + sm.getString("streamProcessor.cancel", > stream.getConnectionId(), > + stream.getIdentifier()), Http2Error.CANCEL, > stream.getIdAsInt()); > + handler.sendStreamReset(se); > + } > + } > } > diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml > index 7a7601c..ae3d717 100644 > --- a/webapps/docs/changelog.xml > +++ b/webapps/docs/changelog.xml > @@ -132,6 +132,11 @@ > query string present in the original HTTP/1.1 request is passed > to the > HTTP/2 request processing. (markt) > </fix> > + <fix> > + When Tomcat writes a final response without reading all of an > HTTP/2 > + request, reset the stream to inform the client that the remaining > + request body is not required. (markt) > + </fix> > </changelog> > </subsection> > <subsection name="Jasper"> > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org > For additional commands, e-mail: dev-h...@tomcat.apache.org > >