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. --- 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