[Git][java-team/httpcomponents-core5][upstream] New upstream version 5.4.3

Jérôme Charaoui (@lavamind) gitlab at salsa.debian.org
Sat Aug 8 05:13:14 BST 2026



Jérôme Charaoui pushed to branch upstream at Debian Java Maintainers / httpcomponents-core5


Commits:
34504ed6 by Jérôme Charaoui at 2026-08-08T00:00:17-04:00
New upstream version 5.4.3
- - - - -


22 changed files:

- RELEASE_NOTES.txt
- httpcore5-h2/pom.xml
- httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
- httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
- httpcore5-reactive/pom.xml
- httpcore5-testing/pom.xml
- httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
- httpcore5/pom.xml
- httpcore5/src/main/java/org/apache/hc/core5/http/config/Http1Config.java
- httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
- httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
- httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncRequesterConsumer.java
- httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
- httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicRequestConsumer.java
- httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicResponseConsumer.java
- httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java
- httpcore5/src/main/java/org/apache/hc/core5/pool/RouteSegmentedConnPool.java
- httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
- httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
- httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLManagedBuffer.java
- + httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPoolLeaseTimeoutRace.java
- pom.xml


Changes:

=====================================
RELEASE_NOTES.txt
=====================================
@@ -1,3 +1,40 @@
+Release 5.4.3
+------------------
+
+This maintenance release fixes several defects and regression reported sicne the previous
+release includin a regression in backpressure handling of async TLS sessions introduced in
+version 5.3.3.
+
+Change Log
+-------------------
+
+* HTTPCORE-796: abort redirected requests during the expect-continue handshake the same
+  way as errors.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Fixed regression in backpressure handling of async TLS sessions. Reverts HTTPCORE-775.
+  Contributed by Ryan Schmitt <rschmitt at apache.org>
+
+* Use max line length of 8192 and max header count of 100 for incoming HTTP/1 messages
+  by default.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Enforce configured HPACK header list size limit upon initialization of HTTP/2
+  connections.
+  Contributed by Arturo Bernal <abernal at apache.org>
+
+* HTTPCORE-774: fixed a race condition caused by concurrent update of the connection input
+  window to the max value (ported from 5.3.x; omitted by mistake).
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Ensure async data consumers can avoid NPE if they have been canceled or released from another
+  thread at the same with concurrent data processing.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Fixed connection pool lease timeout race potentially causing pool entry leak (#649).
+  Contributed by Arturo Bernal <abernal at apache.org>
+
+
 Release 5.4.2
 ------------------
 


=====================================
httpcore5-h2/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents.core5</groupId>
     <artifactId>httpcore5-parent</artifactId>
-    <version>5.4.2</version>
+    <version>5.4.3</version>
   </parent>
   <artifactId>httpcore5-h2</artifactId>
   <name>Apache HttpComponents Core HTTP/2</name>


=====================================
httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java
=====================================
@@ -171,8 +171,7 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
         this.initInputWinSize = H2Config.INIT.getInitialWindowSize();
         this.initOutputWinSize = H2Config.INIT.getInitialWindowSize();
 
-        this.hPackDecoder.setMaxListSize(H2Config.INIT.getMaxHeaderListSize());
-
+        this.hPackDecoder.setMaxListSize(this.localConfig.getMaxHeaderListSize());
         this.lowMark = H2Config.INIT.getInitialWindowSize() / 2;
         this.streamListener = streamListener;
     }
@@ -1492,7 +1491,6 @@ abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnecti
 
         @Override
         public void update(final int increment) throws IOException {
-            incrementInputCapacity(0, connInputWindow, increment);
             incrementInputCapacity(id, inputWindow, increment);
         }
 


=====================================
httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java
=====================================
@@ -28,6 +28,7 @@
 package org.apache.hc.core5.http2.impl.nio;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -46,6 +47,7 @@ import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.nio.AsyncPushProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -62,6 +64,7 @@ import org.apache.hc.core5.http2.frame.FrameFactory;
 import org.apache.hc.core5.http2.frame.FrameType;
 import org.apache.hc.core5.http2.frame.RawFrame;
 import org.apache.hc.core5.http2.frame.StreamIdGenerator;
+import org.apache.hc.core5.http2.hpack.HPackDecoder;
 import org.apache.hc.core5.http2.hpack.HPackEncoder;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.util.ByteArrayBuffer;
@@ -678,7 +681,7 @@ class TestAbstractH2StreamMultiplexer {
         outBuffer.write(continuationFrame3, writableChannel);
 
         Assertions.assertThrows(H2ConnectionException.class, () ->
-            streamMultiplexer.onInput(ByteBuffer.wrap(writableChannel.toByteArray())));
+                streamMultiplexer.onInput(ByteBuffer.wrap(writableChannel.toByteArray())));
     }
 
     @Test
@@ -946,7 +949,7 @@ class TestAbstractH2StreamMultiplexer {
                 new BasicHeader(":authority", "example.test"),
                 new BasicHeader(HttpHeaders.PRIORITY, "u=3,i")
         );
-         mux.createStream(ch, new PriorityHeaderSender(ch, reqHeaders, true));
+        mux.createStream(ch, new PriorityHeaderSender(ch, reqHeaders, true));
 
         // Drive output so the handler submits
         mux.onOutput();
@@ -1069,5 +1072,41 @@ class TestAbstractH2StreamMultiplexer {
         Assertions.assertTrue(idxPriUpd >= 0, "PRIORITY_UPDATE should be emitted when NO_RFC7540=1");
     }
 
+    @Test
+    void testHPackDecoderUsesConfiguredMaxHeaderListSizeImmediately() throws Exception {
+        final int maxHeaderListSize = 128;
+
+        final H2Config h2Config = H2Config.custom()
+                .setMaxHeaderListSize(maxHeaderListSize)
+                .build();
+
+        final ProtocolIOSession ioSession = Mockito.mock(ProtocolIOSession.class);
+        final HttpProcessor httpProcessor = Mockito.mock(HttpProcessor.class);
+        final HandlerFactory<AsyncServerExchangeHandler> handlerFactory = Mockito.mock(HandlerFactory.class);
+
+        final ServerH2StreamMultiplexer multiplexer = new ServerH2StreamMultiplexer(
+                ioSession,
+                httpProcessor,
+                handlerFactory,
+                CharCodingConfig.DEFAULT,
+                h2Config);
+
+        final HPackDecoder hPackDecoder = getHPackDecoder(multiplexer);
+
+        Assertions.assertEquals(maxHeaderListSize, getMaxListSize(hPackDecoder));
+    }
+
+    private static HPackDecoder getHPackDecoder(final AbstractH2StreamMultiplexer multiplexer) throws Exception {
+        final Field field = AbstractH2StreamMultiplexer.class.getDeclaredField("hPackDecoder");
+        field.setAccessible(true);
+        return (HPackDecoder) field.get(multiplexer);
+    }
+
+    private static int getMaxListSize(final HPackDecoder hPackDecoder) throws Exception {
+        final Field field = HPackDecoder.class.getDeclaredField("maxListSize");
+        field.setAccessible(true);
+        return field.getInt(hPackDecoder);
+    }
+
 }
 


=====================================
httpcore5-reactive/pom.xml
=====================================
@@ -27,7 +27,7 @@
   <parent>
     <artifactId>httpcore5-parent</artifactId>
     <groupId>org.apache.httpcomponents.core5</groupId>
-    <version>5.4.2</version>
+    <version>5.4.3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 


=====================================
httpcore5-testing/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents.core5</groupId>
     <artifactId>httpcore5-parent</artifactId>
-    <version>5.4.2</version>
+    <version>5.4.3</version>
   </parent>
   <artifactId>httpcore5-testing</artifactId>
   <name>Apache HttpComponents Core Integration Tests</name>


=====================================
httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
=====================================
@@ -758,6 +758,9 @@ abstract class Http1IntegrationTest extends HttpIntegrationTest {
 
         @Override
         public int write(final ByteBuffer src) throws IOException {
+            if (!channel().isOpen()) {
+                return 0;
+            }
             final int chunk;
             if (!done) {
                 lineBuffer.clear();


=====================================
httpcore5/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents.core5</groupId>
     <artifactId>httpcore5-parent</artifactId>
-    <version>5.4.2</version>
+    <version>5.4.3</version>
   </parent>
   <artifactId>httpcore5</artifactId>
   <name>Apache HttpComponents Core HTTP/1.1</name>


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/config/Http1Config.java
=====================================
@@ -144,8 +144,8 @@ public class Http1Config {
     private static final int INIT_BUF_SIZE = 8192;
     private static final Timeout INIT_WAIT_FOR_CONTINUE = Timeout.ofSeconds(3);
     private static final int INIT_BUF_CHUNK = -1;
-    private static final int INIT_MAX_HEADER_COUNT = -1;
-    private static final int INIT_MAX_LINE_LENGTH = -1;
+    private static final int INIT_MAX_HEADER_COUNT = 100;
+    private static final int INIT_MAX_LINE_LENGTH = 8192;
     private static final int INIT_MAX_EMPTY_LINE_COUNT = 10;
 
     public static class Builder {


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
=====================================
@@ -183,7 +183,7 @@ public class HttpRequestExecutor {
                             }
                             response = null;
                             continue;
-                        } else if (status >= HttpStatus.SC_CLIENT_ERROR) {
+                        } else if (status >= HttpStatus.SC_REDIRECTION) {
                             conn.terminateRequest(request);
                         } else {
                             conn.sendRequestEntity(request);


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
=====================================
@@ -234,7 +234,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
             if (status == HttpStatus.SC_CONTINUE || status >= HttpStatus.SC_SUCCESS) {
                 outputChannel.setSocketTimeout(timeout);
                 requestState.set(MessageState.BODY);
-                if (status < HttpStatus.SC_CLIENT_ERROR) {
+                if (status < HttpStatus.SC_REDIRECTION) {
                     exchangeHandler.produce(internalDataChannel);
                 }
             }
@@ -243,7 +243,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
             return;
         }
         if (requestState.get() == MessageState.BODY) {
-            if (status >= HttpStatus.SC_CLIENT_ERROR) {
+            if (status >= HttpStatus.SC_REDIRECTION) {
                 requestState.set(MessageState.COMPLETE);
                 if (!outputChannel.abortGracefully()) {
                     keepAlive = false;


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncRequesterConsumer.java
=====================================
@@ -113,19 +113,27 @@ public abstract class AbstractAsyncRequesterConsumer<T, E> implements AsyncReque
     @Override
     public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
         final AsyncEntityConsumer<E> dataConsumer = dataConsumerRef.get();
-        dataConsumer.updateCapacity(capacityChannel);
+        if (dataConsumer != null) {
+            dataConsumer.updateCapacity(capacityChannel);
+        } else {
+            capacityChannel.update(Integer.MAX_VALUE);
+        }
     }
 
     @Override
     public final void consume(final ByteBuffer src) throws IOException {
         final AsyncEntityConsumer<E> dataConsumer = dataConsumerRef.get();
-        dataConsumer.consume(src);
+        if (dataConsumer != null) {
+            dataConsumer.consume(src);
+        }
     }
 
     @Override
     public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         final AsyncEntityConsumer<E> dataConsumer = dataConsumerRef.get();
-        dataConsumer.streamEnd(trailers);
+        if (dataConsumer != null) {
+            dataConsumer.streamEnd(trailers);
+        }
     }
 
     @Override


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
=====================================
@@ -174,22 +174,27 @@ public abstract class AbstractServerExchangeHandler<T> implements AsyncServerExc
     @Override
     public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
         final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-        Asserts.notNull(requestConsumer, "Data consumer");
-        requestConsumer.updateCapacity(capacityChannel);
+        if (requestConsumer != null) {
+            requestConsumer.updateCapacity(capacityChannel);
+        } else {
+            capacityChannel.update(Integer.MAX_VALUE);
+        }
     }
 
     @Override
     public final void consume(final ByteBuffer src) throws IOException {
         final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-        Asserts.notNull(requestConsumer, "Data consumer");
-        requestConsumer.consume(src);
+        if (requestConsumer != null) {
+            requestConsumer.consume(src);
+        }
     }
 
     @Override
     public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-        Asserts.notNull(requestConsumer, "Data consumer");
-        requestConsumer.streamEnd(trailers);
+        if (requestConsumer != null) {
+            requestConsumer.streamEnd(trailers);
+        }
     }
 
     @Override


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicRequestConsumer.java
=====================================
@@ -100,19 +100,27 @@ public class BasicRequestConsumer<T> implements AsyncRequestConsumer<Message<Htt
     @Override
     public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.updateCapacity(capacityChannel);
+        if (dataConsumer != null) {
+            dataConsumer.updateCapacity(capacityChannel);
+        } else {
+            capacityChannel.update(Integer.MAX_VALUE);
+        }
     }
 
     @Override
     public void consume(final ByteBuffer src) throws IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.consume(src);
+        if (dataConsumer != null) {
+            dataConsumer.consume(src);
+        }
     }
 
     @Override
     public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.streamEnd(trailers);
+        if (dataConsumer != null) {
+            dataConsumer.streamEnd(trailers);
+        }
     }
 
     @Override


=====================================
httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicResponseConsumer.java
=====================================
@@ -104,19 +104,27 @@ public class BasicResponseConsumer<T> implements AsyncResponseConsumer<Message<H
     @Override
     public void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.updateCapacity(capacityChannel);
+        if (dataConsumer != null) {
+            dataConsumer.updateCapacity(capacityChannel);
+        } else {
+            capacityChannel.update(Integer.MAX_VALUE);
+        }
     }
 
     @Override
     public void consume(final ByteBuffer src) throws IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.consume(src);
+        if (dataConsumer != null) {
+            dataConsumer.consume(src);
+        }
     }
 
     @Override
     public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         final AsyncEntityConsumer<T> dataConsumer = dataConsumerRef.get();
-        dataConsumer.streamEnd(trailers);
+        if (dataConsumer != null) {
+            dataConsumer.streamEnd(trailers);
+        }
     }
 
     @Override


=====================================
httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java
=====================================
@@ -466,8 +466,10 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
                     try {
                         return super.get(timeout, unit);
                     } catch (final TimeoutException ex) {
-                        cancel();
-                        throw ex;
+                        if (cancel()) {
+                            throw ex;
+                        }
+                        return super.get();
                     }
                 }
 


=====================================
httpcore5/src/main/java/org/apache/hc/core5/pool/RouteSegmentedConnPool.java
=====================================
@@ -173,6 +173,19 @@ public final class RouteSegmentedConnPool<R, C extends ModalCloseable> implement
             this.cancelled = false;
             this.timeoutTask = null;
         }
+
+        @Override
+        public PoolEntry<R, C> get(final long timeout, final TimeUnit unit)
+                throws InterruptedException, java.util.concurrent.ExecutionException, TimeoutException {
+            try {
+                return super.get(timeout, unit);
+            } catch (final TimeoutException ex) {
+                if (cancel(true)) {
+                    throw ex;
+                }
+                return super.get();
+            }
+        }
     }
 
     @Override


=====================================
httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
=====================================
@@ -177,8 +177,10 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
                 try {
                     return super.get(timeout, unit);
                 } catch (final TimeoutException ex) {
-                    cancel();
-                    throw ex;
+                    if (cancel()) {
+                        throw ex;
+                    }
+                    return super.get();
                 }
             }
 


=====================================
httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
=====================================
@@ -93,7 +93,6 @@ public class SSLIOSession implements IOSession {
     private final AtomicInteger outboundClosedCount;
     private final AtomicReference<TLSHandShakeState> handshakeStateRef;
     private final IOEventHandler internalEventHandler;
-    private final int packetBufferSize;
 
     private int appEventMask;
 
@@ -180,9 +179,9 @@ public class SSLIOSession implements IOSession {
 
         final SSLSession sslSession = this.sslEngine.getSession();
         // Allocate buffers for network (encrypted) data
-        this.packetBufferSize = sslSession.getPacketBufferSize();
-        this.inEncrypted = SSLManagedBuffer.create(sslBufferMode, packetBufferSize);
-        this.outEncrypted = SSLManagedBuffer.create(sslBufferMode, packetBufferSize);
+        final int netBufferSize = sslSession.getPacketBufferSize();
+        this.inEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize);
+        this.outEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize);
 
         // Allocate buffers for application (unencrypted) data
         final int appBufferSize = sslSession.getApplicationBufferSize();
@@ -670,18 +669,9 @@ public class SSLIOSession implements IOSession {
             if (this.handshakeStateRef.get() == TLSHandShakeState.READY) {
                 return 0;
             }
-
-            for (;;) {
-                final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
-                final SSLEngineResult result = doWrap(src, outEncryptedBuf);
-                if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                    // We don't release the buffer here, it will be expanded (if needed)
-                    // and returned by the next attempt of SSLManagedBuffer#acquire() call.
-                    this.outEncrypted.ensureWriteable(packetBufferSize);
-                } else {
-                    return result.bytesConsumed();
-                }
-            }
+            final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
+            final SSLEngineResult result = doWrap(src, outEncryptedBuf);
+            return result.bytesConsumed();
         } finally {
             this.session.getLock().unlock();
         }


=====================================
httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLManagedBuffer.java
=====================================
@@ -57,54 +57,13 @@ abstract class SSLManagedBuffer {
      */
     abstract boolean hasData();
 
-    /**
-     * Expands the underlying buffer's to make sure it has enough write capacity to accommodate
-     * the required amount of bytes. This method has no side effect if the buffer has enough writeable
-     * capacity left.
-     * @param size the required write capacity
-     */
-    abstract void ensureWriteable(final int size);
-
-    /**
-     * Helper method to ensure additional writeable capacity with respect to the source buffer. It
-     * allocates a new buffer and copies all the data if needed, returning the new buffer. This method
-     * has no side effect if the source buffer has enough writeable capacity left.
-     * @param src source buffer
-     * @param size the required write capacity
-     * @return new buffer (or the source buffer of it  has enough writeable capacity left)
-     */
-    ByteBuffer ensureWriteable(final ByteBuffer src, final int size) {
-        if (src == null) {
-            // Nothing to do, the buffer is not allocated
-            return null;
-        }
-
-        // There is not enough capacity left, we need to expand
-        if (src.remaining() < size) {
-            final int additionalCapacityNeeded = size - src.remaining();
-            final ByteBuffer expanded = ByteBuffer.allocate(src.capacity() + additionalCapacityNeeded);
-
-            // use a duplicated buffer so we don't disrupt the limit of the original buffer
-            final ByteBuffer tmp = src.duplicate();
-            tmp.flip();
-
-            // Copy to expanded buffer
-            expanded.put(tmp);
-
-            // Use a new buffer
-            return expanded;
-        } else {
-            return src;
-        }
-    }
-
     static SSLManagedBuffer create(final SSLBufferMode mode, final int size) {
         return mode == SSLBufferMode.DYNAMIC ? new DynamicBuffer(size) : new StaticBuffer(size);
     }
 
     static final class StaticBuffer extends SSLManagedBuffer {
 
-        private ByteBuffer buffer;
+        private final ByteBuffer buffer;
 
         public StaticBuffer(final int size) {
             Args.positive(size, "size");
@@ -131,10 +90,6 @@ abstract class SSLManagedBuffer {
             return buffer.position() > 0;
         }
 
-        @Override
-        void ensureWriteable(final int size) {
-            buffer = ensureWriteable(buffer, size);
-        }
     }
 
     static final class DynamicBuffer extends SSLManagedBuffer {
@@ -171,10 +126,6 @@ abstract class SSLManagedBuffer {
             return wrapped != null && wrapped.position() > 0;
         }
 
-        @Override
-        void ensureWriteable(final int size) {
-            wrapped = ensureWriteable(wrapped, size);
-        }
     }
 
 }


=====================================
httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPoolLeaseTimeoutRace.java
=====================================
@@ -0,0 +1,160 @@
+/*
+ * ====================================================================
+ * 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.pool;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.apache.hc.core5.http.SocketModalCloseable;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class TestConnPoolLeaseTimeout {
+
+    private static final Timeout TIMEOUT = Timeout.ofSeconds(30);
+
+    static final class DummyConn implements SocketModalCloseable {
+
+        private volatile Timeout socketTimeout;
+
+        @Override
+        public Timeout getSocketTimeout() {
+            return socketTimeout;
+        }
+
+        @Override
+        public void setSocketTimeout(final Timeout timeout) {
+            this.socketTimeout = timeout;
+        }
+
+        @Override
+        public void close(final CloseMode closeMode) {
+        }
+
+        @Override
+        public void close() throws IOException {
+        }
+    }
+
+    static final class PoolCase {
+        final String name;
+        final Supplier<ManagedConnPool<String, DummyConn>> supplier;
+
+        PoolCase(final String name, final Supplier<ManagedConnPool<String, DummyConn>> supplier) {
+            this.name = name;
+            this.supplier = supplier;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    static Stream<PoolCase> pools() {
+        return Stream.of(
+                new PoolCase("STRICT", () -> new StrictConnPool<>(1, 1)),
+                new PoolCase("LAX", () -> new LaxConnPool<>(1)),
+                new PoolCase("OFFLOCK", () -> new RouteSegmentedConnPool<>(
+                        1,
+                        1,
+                        TimeValue.NEG_ONE_MILLISECOND,
+                        PoolReusePolicy.LIFO,
+                        new DefaultDisposalCallback<>()))
+        );
+    }
+
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("pools")
+    @org.junit.jupiter.api.Timeout(60)
+    void testLeaseTimeoutDoesNotLeakLeasedEntries(final PoolCase poolCase) throws Exception {
+        final ManagedConnPool<String, DummyConn> pool = poolCase.supplier.get();
+
+        final String route = "route-1";
+        final Timeout requestTimeout = Timeout.ofMicroseconds(1);
+
+        final int concurrentThreads = 10;
+        final CountDownLatch countDownLatch = new CountDownLatch(concurrentThreads);
+        final AtomicLong n = new AtomicLong(concurrentThreads * 100);
+
+        final ExecutorService executorService = Executors.newFixedThreadPool(concurrentThreads);
+        final AtomicReference<Exception> unexpectedException = new AtomicReference<>();
+        try {
+            for (int i = 0; i < concurrentThreads; i++) {
+                executorService.execute(() -> {
+                    try {
+                        while (n.decrementAndGet() > 0) {
+                            final Future<PoolEntry<String, DummyConn>> f = pool.lease(route, null, requestTimeout, null);
+                            try {
+                                final PoolEntry<String, DummyConn> entry =
+                                        f.get(requestTimeout.getDuration(), requestTimeout.getTimeUnit());
+                                pool.release(entry, true);
+                            } catch (final InterruptedException ex) {
+                                Thread.currentThread().interrupt();
+                                unexpectedException.compareAndSet(null, ex);
+                            } catch (final TimeoutException ex) {
+                                f.cancel(true);
+                            } catch (final ExecutionException ex) {
+                                f.cancel(true);
+                                if (!(ex.getCause() instanceof TimeoutException)) {
+                                    unexpectedException.compareAndSet(null, ex);
+                                }
+                            }
+                        }
+                    } finally {
+                        countDownLatch.countDown();
+                    }
+                });
+            }
+
+            Assertions.assertTrue(countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()));
+            Assertions.assertTrue(n.get() <= 0);
+            Assertions.assertNull(unexpectedException.get());
+
+            final PoolStats stats = pool.getStats(route);
+            Assertions.assertEquals(0, stats.getLeased());
+
+        } finally {
+            executorService.shutdownNow();
+            executorService.awaitTermination(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+            pool.close(CloseMode.GRACEFUL);
+        }
+    }
+}
\ No newline at end of file


=====================================
pom.xml
=====================================
@@ -33,7 +33,7 @@
   <groupId>org.apache.httpcomponents.core5</groupId>
   <artifactId>httpcore5-parent</artifactId>
   <name>Apache HttpComponents Core</name>
-  <version>5.4.2</version>
+  <version>5.4.3</version>
   <description>Apache HttpComponents Core is a library of components for building HTTP enabled services</description>
   <url>https://hc.apache.org/httpcomponents-core-5.4.x/${project.version}/</url>
   <inceptionYear>2005</inceptionYear>
@@ -48,7 +48,7 @@
     <connection>scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-core.git</connection>
     <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-core.git</developerConnection>
     <url>https://github.com/apache/httpcomponents-core/tree/${project.scm.tag}</url>
-    <tag>5.4.2</tag>
+    <tag>5.4.3</tag>
   </scm>
 
   <distributionManagement>



View it on GitLab: https://salsa.debian.org/java-team/httpcomponents-core5/-/commit/34504ed6c9f04e2b6390550c49ec6c24b1deab02

-- 
View it on GitLab: https://salsa.debian.org/java-team/httpcomponents-core5/-/commit/34504ed6c9f04e2b6390550c49ec6c24b1deab02
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20260808/5d7f0f91/attachment.htm>


More information about the pkg-java-commits mailing list