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 2026/09/20 20:06:40 UTC

(httpcomponents-core) branch master updated: Async data exchnage handlers to signal that the opposite endpoint requesed graceful outgoing data stream termination

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 53513fca9 Async data exchnage handlers to signal that the opposite endpoint requesed graceful outgoing data stream termination
53513fca9 is described below

commit 53513fca992633057ddb929305ba81b7d3a98a12
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sun Sep 20 15:38:12 2026 +0200

    Async data exchnage handlers to signal that the opposite endpoint requesed graceful outgoing data stream termination
---
 .../apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java    | 7 +++++++
 .../hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java       | 4 ++++
 .../main/java/org/apache/hc/core5/http2/impl/nio/H2Stream.java   | 1 +
 .../java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java | 2 ++
 .../org/apache/hc/core5/http2/impl/nio/NoopH2StreamHandler.java  | 4 ++++
 .../apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java    | 7 +++++++
 .../hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java       | 4 ++++
 .../hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java | 1 +
 .../apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java  | 1 +
 .../org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java   | 9 +++++++++
 .../http/nio/support/BasicAsyncServerExpectationDecorator.java   | 5 +++++
 11 files changed, 45 insertions(+)

diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java
index 7871dd4cb..6393e5991 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java
@@ -185,6 +185,13 @@ public void produceOutput() throws HttpException, IOException {
         }
     }
 
+    @Override
+    public void outputAborted() {
+        if (requestState.get() == MessageState.BODY) {
+            exchangeHandler.outputAborted();
+        }
+    }
+
     @Override
     public void consumePromise(final List<Header> headers) throws HttpException, IOException {
         throw new ProtocolException("Unexpected message promise");
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java
index 3b32536ca..280be311f 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java
@@ -102,6 +102,10 @@ public boolean isOutputReady() {
     public void produceOutput() throws HttpException, IOException {
     }
 
+    @Override
+    public void outputAborted() {
+    }
+
     @Override
     public void consumePromise(final List<Header> headers) throws HttpException, IOException {
         if (requestState == MessageState.HEADERS) {
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2Stream.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2Stream.java
index 7bd10613d..f946a3665 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2Stream.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2Stream.java
@@ -288,6 +288,7 @@ boolean abort() {
     boolean abortGracefully() throws IOException {
         if (!isLocalClosed() && isRemoteClosed()) {
             channel.endStream();
+            handler.outputAborted();
             releaseResources();
             return true;
         } else {
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java
index 488727df4..316d65930 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java
@@ -54,6 +54,8 @@ interface H2StreamHandler extends ResourceHolder {
 
     void failed(Exception cause);
 
+    void outputAborted();
+
     void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException;
 
 }
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
index 5bbc3d47a..7bc975135 100644
--- 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
@@ -48,6 +48,10 @@ public boolean isOutputReady() {
     public void produceOutput() throws HttpException, IOException {
     }
 
+    @Override
+    public void outputAborted() {
+    }
+
     @Override
     public void consumePromise(final List<Header> headers) throws HttpException, IOException {
     }
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java
index a51aea1e6..3eefa0d27 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java
@@ -333,6 +333,13 @@ public void produceOutput() throws HttpException, IOException {
         }
     }
 
+    @Override
+    public void outputAborted() {
+        if (responseState.get() == MessageState.BODY) {
+            exchangeHandler.outputAborted();
+        }
+    }
+
     @Override
     public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException {
         if (done.get()) {
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java
index 6e4fc9d89..37a5c29d0 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java
@@ -236,6 +236,10 @@ public void terminateExchange() {
         }
     }
 
+    @Override
+    public void outputAborted() {
+    }
+
     @Override
     public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException {
         throw ex;
diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
index 5eef30f19..7c7ad7919 100644
--- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
+++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
@@ -957,6 +957,7 @@ static final class PriorityHeaderSender implements H2StreamHandler {
                 sent = true;
             }
         }
+        @Override public void outputAborted() { }
         @Override public void consumePromise(final List<Header> headers) { }
         @Override public void consumeHeader(final List<Header> headers, final boolean endStream) { }
         @Override public void updateInputCapacity() { }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
index 8206fd3f7..f750c2a73 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
@@ -246,6 +246,7 @@ void consumeHeader(final HttpResponse response, final EntityDetails entityDetail
             if (status >= HttpStatus.SC_REDIRECTION) {
                 requestState.set(MessageState.COMPLETE);
                 if (!outputChannel.abortGracefully()) {
+                    exchangeHandler.outputAborted();
                     keepAlive = false;
                 }
             }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java
index e942e3909..ba53e4950 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java
@@ -41,4 +41,13 @@ public interface AsyncDataExchangeHandler extends AsyncDataConsumer, AsyncDataPr
      */
     void failed(Exception cause);
 
+    /**
+     * Triggered to signal the opposite endpoint requesed graceful
+     * termination of the outgoing data stream.
+     *
+     * @since 5.5
+     */
+    default void outputAborted() {
+    }
+
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
index 326e61839..76aed1629 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
@@ -141,6 +141,11 @@ public final void produce(final DataStreamChannel channel) throws IOException {
         }
     }
 
+    @Override
+    public void outputAborted() {
+        handler.outputAborted();
+    }
+
     @Override
     public final void failed(final Exception cause) {
         if (exceptionCallback != null) {