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:18:41 UTC
[httpcomponents-core] branch 5.1.x updated (d78555c -> 06751c1)
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a change to branch 5.1.x
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git.
from d78555c HTTPCORE-694: Fixed decrypted data check by non-blocking SSL i/o sessions (#317)
new 7ae878d HTTPCORE-696: H2 protocol handler to ensure minimal processing of incoming frames during a graceful shutdown.
new 06751c1 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 | 71 +++++++++++++++-------
...StreamHandler.java => NoopH2StreamHandler.java} | 47 ++++++++++----
2 files changed, 85 insertions(+), 33 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 5.1.x
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 7ae878d018f630cd635662c8954924200614b830
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 | 65 +++++++++++------
.../core5/http2/impl/nio/NoopH2StreamHandler.java | 84 ++++++++++++++++++++++
2 files changed, 128 insertions(+), 21 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 6229b07..885238f 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
@@ -26,7 +26,6 @@
*/
package org.apache.hc.core5.http2.impl.nio;
-import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
@@ -43,6 +42,8 @@ import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.ssl.SSLSession;
+
import org.apache.hc.core5.concurrent.Cancellable;
import org.apache.hc.core5.concurrent.CancellableDependency;
import org.apache.hc.core5.http.ConnectionClosedException;
@@ -133,6 +134,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
private int processedRemoteStreamId;
private EndpointDetails endpointDetails;
+ private boolean goAwayReceived;
AbstractH2StreamMultiplexer(
final ProtocolIOSession ioSession,
@@ -503,19 +505,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()) {
@@ -722,12 +735,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);
@@ -753,12 +760,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();
@@ -913,6 +933,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");
}
@@ -939,8 +963,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);
@@ -963,6 +994,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(); ) {
@@ -1039,9 +1071,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);
@@ -1077,9 +1106,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();
}
@@ -1101,9 +1127,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 5.1.x
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 06751c1f6713c5b0df5a3b1293484b0c45742714
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 885238f..dc73741 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
@@ -749,6 +749,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
if (stream.isTerminated()) {
streamMap.remove(streamId);
stream.releaseResources();
+ requestSessionOutput();
}
}
break;
@@ -803,6 +804,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
if (stream.isTerminated()) {
streamMap.remove(streamId);
stream.releaseResources();
+ requestSessionOutput();
}
}
break;
@@ -827,6 +829,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
if (stream.isTerminated()) {
streamMap.remove(streamId);
stream.releaseResources();
+ requestSessionOutput();
}
}
break;
@@ -876,6 +879,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
stream.reset(new H2StreamResetException(errorCode, "Stream reset (" + errorCode + ")"));
streamMap.remove(streamId);
stream.releaseResources();
+ requestSessionOutput();
}
}
break;
@@ -1207,6 +1211,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
if (stream.isTerminated()) {
it.remove();
stream.releaseResources();
+ requestSessionOutput();
}
if (!outputQueue.isEmpty()) {
break;
@@ -1694,7 +1699,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
void releaseResources() {
handler.releaseResources();
- channel.requestOutput();
}
void appendState(final StringBuilder buf) {