You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2021/11/10 21:28:45 UTC

[httpcomponents-core] branch master updated (5b6f3dd -> 6f7d0d1)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git.


    from 5b6f3dd  Fixed #format in Deadline; improved #hashCode; replaced SimpleDatteFormat with Java 8 Time APIs
     new 8629781  HTTPCORE-696: H2 protocol handler to ensure minimal processing of incoming frames during a graceful shutdown.
     new 6f7d0d1  H2 stream resource release optimization

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../impl/nio/AbstractH2StreamMultiplexer.java      | 68 +++++++++++++++-------
 ...StreamHandler.java => NoopH2StreamHandler.java} | 47 +++++++++++----
 2 files changed, 83 insertions(+), 32 deletions(-)
 copy httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/{H2StreamHandler.java => NoopH2StreamHandler.java} (57%)

[httpcomponents-core] 01/02: HTTPCORE-696: H2 protocol handler to ensure minimal processing of incoming frames during a graceful shutdown.

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit 8629781127259e1c337ac9079f825eaaa6ba6601
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Nov 10 11:22:54 2021 +0100

    HTTPCORE-696: H2 protocol handler to ensure minimal processing of incoming frames during a graceful shutdown.
---
 .../impl/nio/AbstractH2StreamMultiplexer.java      | 62 ++++++++++------
 .../core5/http2/impl/nio/NoopH2StreamHandler.java  | 84 ++++++++++++++++++++++
 2 files changed, 126 insertions(+), 20 deletions(-)

diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
index b59e315..df709ae 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
@@ -133,6 +133,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
 
     private int processedRemoteStreamId;
     private EndpointDetails endpointDetails;
+    private boolean goAwayReceived;
 
     AbstractH2StreamMultiplexer(
             final ProtocolIOSession ioSession,
@@ -503,19 +504,30 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             processPendingCommands();
         }
         if (connState.compareTo(ConnectionHandshake.GRACEFUL_SHUTDOWN) == 0) {
+            int liveStreams = 0;
             for (final Iterator<Map.Entry<Integer, H2Stream>> it = streamMap.entrySet().iterator(); it.hasNext(); ) {
                 final Map.Entry<Integer, H2Stream> entry = it.next();
                 final H2Stream stream = entry.getValue();
                 if (stream.isLocalClosed() && stream.isRemoteClosed()) {
                     stream.releaseResources();
                     it.remove();
+                } else {
+                    if (idGenerator.isSameSide(stream.getId()) || stream.getId() <= processedRemoteStreamId) {
+                        liveStreams++;
+                    }
                 }
             }
-            if (streamMap.isEmpty()) {
+            if (liveStreams == 0) {
                 connState = ConnectionHandshake.SHUTDOWN;
             }
         }
         if (connState.compareTo(ConnectionHandshake.SHUTDOWN) >= 0) {
+            if (!streamMap.isEmpty()) {
+                for (final H2Stream stream : streamMap.values()) {
+                    stream.releaseResources();
+                }
+                streamMap.clear();
+            }
             ioSession.getLock().lock();
             try {
                 if (outputBuffer.isEmpty() && outputQueue.isEmpty()) {
@@ -715,12 +727,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
         if (continuation != null && frameType != FrameType.CONTINUATION) {
             throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "CONTINUATION frame expected");
         }
-        if (connState.compareTo(ConnectionHandshake.GRACEFUL_SHUTDOWN) >= 0) {
-            if (streamId > processedRemoteStreamId && !idGenerator.isSameSide(streamId)) {
-                // ignore the frame
-                return;
-            }
-        }
         switch (frameType) {
             case DATA: {
                 final H2Stream stream = getValidStream(streamId);
@@ -746,12 +752,25 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                 if (stream == null) {
                     acceptHeaderFrame();
 
+                    if (idGenerator.isSameSide(streamId)) {
+                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId);
+                    }
+                    if (goAwayReceived ) {
+                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received");
+                    }
+
                     updateLastStreamId(streamId);
 
                     final H2StreamChannelImpl channel = new H2StreamChannelImpl(
                             streamId, false, initInputWinSize, initOutputWinSize);
-                    final H2StreamHandler streamHandler = createRemotelyInitiatedStream(
-                            channel, httpProcessor, connMetrics, null);
+                    final H2StreamHandler streamHandler;
+                    if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
+                        streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics, null);
+                    } else {
+                        streamHandler = NoopH2StreamHandler.INSTANCE;
+                        channel.setLocalEndStream();
+                    }
+
                     stream = new H2Stream(channel, streamHandler, true);
                     if (stream.isOutputReady()) {
                         stream.produceOutput();
@@ -906,6 +925,10 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             case PUSH_PROMISE: {
                 acceptPushFrame();
 
+                if (goAwayReceived ) {
+                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received");
+                }
+
                 if (!localConfig.isPushEnabled()) {
                     throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push is disabled");
                 }
@@ -932,8 +955,15 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
 
                 final H2StreamChannelImpl channel = new H2StreamChannelImpl(
                         promisedStreamId, false, initInputWinSize, initOutputWinSize);
-                final H2StreamHandler streamHandler = createRemotelyInitiatedStream(
-                        channel, httpProcessor, connMetrics, stream.getPushHandlerFactory());
+                final H2StreamHandler streamHandler;
+                if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
+                    streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics,
+                            stream.getPushHandlerFactory());
+                } else {
+                    streamHandler = NoopH2StreamHandler.INSTANCE;
+                    channel.setLocalEndStream();
+                }
+
                 final H2Stream promisedStream = new H2Stream(channel, streamHandler, true);
                 streamMap.put(promisedStreamId, promisedStream);
 
@@ -956,6 +986,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                 }
                 final int processedLocalStreamId = payload.getInt();
                 final int errorCode = payload.getInt();
+                goAwayReceived = true;
                 if (errorCode == H2Error.NO_ERROR.getCode()) {
                     if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
                         for (final Iterator<Map.Entry<Integer, H2Stream>> it = streamMap.entrySet().iterator(); it.hasNext(); ) {
@@ -1032,9 +1063,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             if (streamListener != null) {
                 streamListener.onHeaderInput(this, promisedStreamId, headers);
             }
-            if (connState == ConnectionHandshake.GRACEFUL_SHUTDOWN) {
-                throw new H2StreamResetException(H2Error.REFUSED_STREAM, "Stream refused");
-            }
             promisedStream.consumePromise(headers);
         } else {
             continuation.copyPayload(payload);
@@ -1070,9 +1098,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             if (stream.isLocalReset()) {
                 return;
             }
-            if (connState == ConnectionHandshake.GRACEFUL_SHUTDOWN) {
-                throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, "Stream refused");
-            }
             if (frame.isFlagSet(FrameFlag.END_STREAM)) {
                 stream.setRemoteEndStream();
             }
@@ -1094,9 +1119,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             if (streamListener != null) {
                 streamListener.onHeaderInput(this, streamId, headers);
             }
-            if (connState == ConnectionHandshake.GRACEFUL_SHUTDOWN) {
-                throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, "Stream refused");
-            }
             if (stream.isRemoteClosed()) {
                 throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed");
             }
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/NoopH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/NoopH2StreamHandler.java
new file mode 100644
index 0000000..5bbc3d4
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/NoopH2StreamHandler.java
@@ -0,0 +1,84 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http2.impl.nio;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.nio.AsyncPushConsumer;
+import org.apache.hc.core5.http.nio.HandlerFactory;
+
+final class NoopH2StreamHandler implements H2StreamHandler {
+
+    static final NoopH2StreamHandler INSTANCE = new NoopH2StreamHandler();
+
+    @Override
+    public boolean isOutputReady() {
+        return false;
+    }
+
+    @Override
+    public void produceOutput() throws HttpException, IOException {
+    }
+
+    @Override
+    public void consumePromise(final List<Header> headers) throws HttpException, IOException {
+    }
+
+    @Override
+    public void consumeHeader(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
+    }
+
+    @Override
+    public void updateInputCapacity() throws IOException {
+    }
+
+    @Override
+    public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException {
+    }
+
+    @Override
+    public HandlerFactory<AsyncPushConsumer> getPushHandlerFactory() {
+        return null;
+    }
+
+    @Override
+    public void failed(final Exception cause) {
+    }
+
+    @Override
+    public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException {
+    }
+
+    @Override
+    public void releaseResources() {
+    }
+
+}

[httpcomponents-core] 02/02: H2 stream resource release optimization

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit 6f7d0d13e2db1a0fc4952fc201ad7bfc4a9e66c7
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Nov 10 12:01:58 2021 +0100

    H2 stream resource release optimization
---
 .../apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
index df709ae..a53e33b 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
@@ -741,6 +741,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                 if (stream.isTerminated()) {
                     streamMap.remove(streamId);
                     stream.releaseResources();
+                    requestSessionOutput();
                 }
             }
             break;
@@ -795,6 +796,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                 if (stream.isTerminated()) {
                     streamMap.remove(streamId);
                     stream.releaseResources();
+                    requestSessionOutput();
                 }
             }
             break;
@@ -819,6 +821,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                 if (stream.isTerminated()) {
                     streamMap.remove(streamId);
                     stream.releaseResources();
+                    requestSessionOutput();
                 }
             }
             break;
@@ -868,6 +871,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
                     stream.reset(new H2StreamResetException(errorCode, "Stream reset (" + errorCode + ")"));
                     streamMap.remove(streamId);
                     stream.releaseResources();
+                    requestSessionOutput();
                 }
             }
             break;
@@ -1199,6 +1203,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
             if (stream.isTerminated()) {
                 it.remove();
                 stream.releaseResources();
+                requestSessionOutput();
             }
             if (!outputQueue.isEmpty()) {
                 break;
@@ -1686,7 +1691,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
 
         void releaseResources() {
             handler.releaseResources();
-            channel.requestOutput();
         }
 
         void appendState(final StringBuilder buf) {