On Mon, Apr 8, 2019 at 11:05 AM Mark Thomas <ma...@apache.org> wrote:

> On 08/04/2019 09:57, Rémy Maucherat wrote:
> > 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.
>
> I saw that failure but couldn't reproduce it locally. I'll take another
> look.
>

It seems ok to ignore it for now, it doesn't really look related although I
didn't get it before.

With logging, it goes like this at the end of the test:
FAIL:
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:method] with
value [GET]
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:scheme] with
value [http]
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:path] with
value [/simple]
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:authority]
with value [localhost:41523]
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[3], Frame type [HEADERS], Flags [4], Payload size [37]
08-Apr-2019 10:50:18.982 FINE [http-nio2-127.0.0.1-auto-1-exec-4]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [0], Stream
[5], Frame type [HEADERS], Flags [5], Payload size [4]
08-Apr-2019 10:50:18.982 FINE [main]
org.apache.coyote.http2.Http2Parser.readHeaderPayload Connection [-1],
Stream [3], Processing headers payload of size [37]
08-Apr-2019 10:50:18.983 FINE [main]
org.apache.coyote.http2.Http2Parser.swallow Connection [-1], Stream [3],
Swallowed [0] bytes
08-Apr-2019 10:50:18.983 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[3], Frame type [DATA], Flags [1], Payload size [8192]
08-Apr-2019 10:50:18.983 FINE [main]
org.apache.coyote.http2.Http2Parser.readDataFrame Connection [-1], Stream
[3], Data length, [8192], Padding length [none]
08-Apr-2019 10:50:18.983 FINE [main]
org.apache.coyote.http2.Http2Parser.swallow Connection [-1], Stream [3],
Swallowed [8192] bytes
>>>> 08-Apr-2019 10:50:18.983 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[3], Frame type [RST], Flags [0], Payload size [4]
08-Apr-2019 10:50:18.984 INFO [main]
org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler
["http-nio2-127.0.0.1-auto-1-41523"]
08-Apr-2019 10:50:18.984 INFO [main]
org.apache.catalina.core.StandardService.stopInternal Stopping service
[Tomcat]
08-Apr-2019 10:50:18.996 INFO [main]
org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler
["http-nio2-127.0.0.1-auto-1-41523"]

vs PASS:
08-Apr-2019 15:06:16.315 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:method] with
value [GET]
08-Apr-2019 15:06:16.316 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:scheme] with
value [http]
08-Apr-2019 15:06:16.317 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:path] with
value [/simple]
08-Apr-2019 15:06:16.317 FINE [main]
org.apache.coyote.http2.HpackEncoder.encode Encoding header [:authority]
with value [localhost:34997]
08-Apr-2019 15:06:16.318 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[3], Frame type [HEADERS], Flags [4], Payload size [37]
08-Apr-2019 15:06:16.318 FINE [http-nio2-127.0.0.1-auto-1-exec-6]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [0], Stream
[5], Frame type [HEADERS], Flags [5], Payload size [4]
08-Apr-2019 15:06:16.318 FINE [main]
org.apache.coyote.http2.Http2Parser.readHeaderPayload Connection [-1],
Stream [3], Processing headers payload of size [37]
08-Apr-2019 15:06:16.319 FINE [main]
org.apache.coyote.http2.Http2Parser.swallow Connection [-1], Stream [3],
Swallowed [0] bytes
08-Apr-2019 15:06:16.320 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[3], Frame type [DATA], Flags [1], Payload size [8192]
08-Apr-2019 15:06:16.320 FINE [main]
org.apache.coyote.http2.Http2Parser.readDataFrame Connection [-1], Stream
[3], Data length, [8192], Padding length [none]
08-Apr-2019 15:06:16.321 FINE [main]
org.apache.coyote.http2.Http2Parser.swallow Connection [-1], Stream [3],
Swallowed [8192] bytes
08-Apr-2019 15:06:16.358 FINE [http-nio2-127.0.0.1-auto-1-exec-4]
org.apache.coyote.http2.Http2UpgradeHandler.headersStart Connection [0],
Stream [5], Stream ignored as no new streams are permitted on this
connection
08-Apr-2019 15:06:16.358 FINE [main]
org.apache.coyote.http2.Http2Parser.validateFrame Connection [-1], Stream
[0], Frame type [GOAWAY], Flags [0], Payload size [8]
08-Apr-2019 15:06:16.359 FINE [http-nio2-127.0.0.1-auto-1-exec-4]
org.apache.coyote.http2.Http2Parser.readHeaderPayload Connection [0],
Stream [5], Processing headers payload of size [4]
08-Apr-2019 15:06:16.359 FINE [http-nio2-127.0.0.1-auto-1-exec-4]
org.apache.coyote.http2.Http2Parser.swallow Connection [0], Stream [5],
Swallowed [0] bytes
08-Apr-2019 15:06:16.359 INFO [main]
org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler
["http-nio2-127.0.0.1-auto-1-34997"]
08-Apr-2019 15:06:16.359 FINE [http-nio2-127.0.0.1-auto-1-exec-4]
org.apache.coyote.http2.Http2UpgradeHandler.upgradeDispatch Entry,
Connection [0], SocketStatus [OPEN_READ]
08-Apr-2019 15:06:16.359 INFO [main]
org.apache.catalina.core.StandardService.stopInternal Stopping service
[Tomcat]

So the server is reading a reset frame first when it fails I guess. It
doesn't seem broken to me overall. If you agree, maybe the test should
ignore the last frame or allow a reset as well ?

Note: I don't get the failure with the new NIO async code.

Rémy


>
> Mark
>
>
> >
> > 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
> >>
> >>
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
>
>

Reply via email to