[Git][java-team/httpcomponents-core][upstream] New upstream version 4.4.12

Emmanuel Bourg gitlab at salsa.debian.org
Sat Oct 5 09:07:09 BST 2019



Emmanuel Bourg pushed to branch upstream at Debian Java Maintainers / httpcomponents-core


Commits:
acce30fe by Emmanuel Bourg at 2019-10-05T07:58:57Z
New upstream version 4.4.12
- - - - -


23 changed files:

- RELEASE_NOTES.txt
- httpcore-ab/pom.xml
- httpcore-nio/pom.xml
- httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/IdentityDecoder.java
- httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/LengthDelimitedDecoder.java
- httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/AbstractIOReactor.java
- httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/BaseIOReactor.java
- httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/DefaultConnectingIOReactor.java
- httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionRequestImpl.java
- httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java
- httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestIdentityDecoder.java
- httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestLengthDelimitedDecoder.java
- httpcore-nio/src/test/java/org/apache/http/nio/integration/TestCustomSSL.java → httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java
- httpcore-osgi/pom.xml
- httpcore/pom.xml
- httpcore/src/main/java/org/apache/http/config/SocketConfig.java
- httpcore/src/main/java/org/apache/http/entity/ContentType.java
- httpcore/src/main/java/org/apache/http/impl/pool/BasicConnFactory.java
- httpcore/src/main/java/org/apache/http/message/HeaderGroup.java
- httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
- httpcore/src/main/java/org/apache/http/util/ByteArrayBuffer.java
- httpcore/src/test/java/org/apache/http/util/TestByteArrayBuffer.java
- pom.xml


Changes:

=====================================
RELEASE_NOTES.txt
=====================================
@@ -1,3 +1,42 @@
+Release 4.4.12
+-------------------
+
+This is a maintenance release that corrects a number of defects discovered since release 4.4.11.
+
+Changelog
+-------------------
+* HTTPASYNC-152: Minimize possibility of a race condition when I/O reactor session request gets
+  cancelled or times out immediately after its creation.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Execute Socket[Channel]#connect under doPrivileged.
+  Contributed by Simon Willnauer <simonw at apache.org>
+
+* Bug fix: Ensure consistency of internal buffers in case of I/O or SSL exception (back-ported
+  from master).
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Rethrow RuntimeExceptions thrown by SSLSetupHandler#initalize as SSLException
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* HTTPCORE-578: Incorrect serialization of HeaderGroup.
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* HTTPCORE-370, HTTPCORE-574: Discard non-blocking session request immediately upon timeout.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* HTTPCLIENT-1978: Filter characters before byte conversion.
+  Contributed by Ryan Schmitt <rschmitt at apache.org>
+
+* HTTPCORE-573 FileContentDecoder doesn't always enforce the maximum number of bytes to transfer.
+  Contributed by Julien Coloos <julien.coloos at gmail.com>
+
+* HTTPCORE-567: Fixed race condition that may cause a connection leak when the connection lease
+  request is cancelled from another thread.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+
 Release 4.4.11
 -------------------
 


=====================================
httpcore-ab/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-core</artifactId>
-    <version>4.4.11</version>
+    <version>4.4.12</version>
   </parent>
   <artifactId>httpcore-ab</artifactId>
   <name>Apache HttpCore Benchmarking Tool</name>
@@ -88,7 +88,6 @@
 
       <plugin>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>${hc.project-info.version}</version>
         <inherited>false</inherited>
         <reportSets>
           <reportSet>


=====================================
httpcore-nio/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-core</artifactId>
-    <version>4.4.11</version>
+    <version>4.4.12</version>
   </parent>
   <artifactId>httpcore-nio</artifactId>
   <name>Apache HttpCore NIO</name>
@@ -120,7 +120,6 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -141,7 +140,6 @@
 
       <plugin>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>${hc.project-info.version}</version>
         <inherited>false</inherited>
         <reportSets>
           <reportSet>
@@ -156,12 +154,10 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
-        <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
-        <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/IdentityDecoder.java
=====================================
@@ -93,8 +93,9 @@ public class IdentityDecoder extends AbstractContentDecoder
 
         long bytesRead;
         if (this.buffer.hasData()) {
+            final int maxLen = this.buffer.length();
             dst.position(position);
-            bytesRead = this.buffer.read(dst);
+            bytesRead = this.buffer.read(dst, count < maxLen ? (int)count : maxLen);
         } else {
             if (this.channel.isOpen()) {
                 if (position > dst.size()) {


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/LengthDelimitedDecoder.java
=====================================
@@ -117,7 +117,7 @@ public class LengthDelimitedDecoder extends AbstractContentDecoder
         if (this.buffer.hasData()) {
             final int maxLen = Math.min(chunk, this.buffer.length());
             dst.position(position);
-            bytesRead = this.buffer.read(dst, maxLen);
+            bytesRead = this.buffer.read(dst, count < maxLen ? (int)count : maxLen);
         } else {
             if (this.channel.isOpen()) {
                 if (position > dst.size()) {


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/AbstractIOReactor.java
=====================================
@@ -419,14 +419,23 @@ public abstract class AbstractIOReactor implements IOReactor {
             }
             try {
                 this.sessions.add(session);
+                key.attach(session);
                 final SessionRequestImpl sessionRequest = entry.getSessionRequest();
                 if (sessionRequest != null) {
-                    sessionRequest.completed(session);
+                    if (!sessionRequest.isTerminated()) {
+                        sessionRequest.completed(session);
+                    }
+                    if (!sessionRequest.isTerminated()) {
+                        sessionCreated(key, session);
+                    }
+                    if (sessionRequest.isTerminated()) {
+                        throw new CancelledKeyException();
+                    }
+                } else {
+                    sessionCreated(key, session);
                 }
-                key.attach(session);
-                sessionCreated(key, session);
             } catch (final CancelledKeyException ex) {
-                queueClosedSession(session);
+                session.close();
                 key.attach(null);
             }
         }
@@ -489,7 +498,12 @@ public abstract class AbstractIOReactor implements IOReactor {
             final int timeout = session.getSocketTimeout();
             if (timeout > 0) {
                 if (session.getLastAccessTime() + timeout < now) {
-                    sessionTimedOut(session);
+                    try {
+                        sessionTimedOut(session);
+                    } catch (final CancelledKeyException ex) {
+                        session.close();
+                        key.attach(null);
+                    }
                 }
             }
         }


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/BaseIOReactor.java
=====================================
@@ -169,8 +169,7 @@ public class BaseIOReactor extends AbstractIOReactor {
                 this.bufferingSessions.add(session);
             }
         } catch (final CancelledKeyException ex) {
-            queueClosedSession(session);
-            key.attach(null);
+            throw ex;
         } catch (final RuntimeException ex) {
             handleRuntimeException(ex);
         }
@@ -187,8 +186,7 @@ public class BaseIOReactor extends AbstractIOReactor {
         try {
             this.eventDispatch.outputReady(session);
         } catch (final CancelledKeyException ex) {
-            queueClosedSession(session);
-            key.attach(null);
+            throw ex;
         } catch (final RuntimeException ex) {
             handleRuntimeException(ex);
         }
@@ -230,7 +228,7 @@ public class BaseIOReactor extends AbstractIOReactor {
                     }
                 } catch (final CancelledKeyException ex) {
                     it.remove();
-                    queueClosedSession(session);
+                    session.close();
                 } catch (final RuntimeException ex) {
                     handleRuntimeException(ex);
                 }
@@ -247,7 +245,7 @@ public class BaseIOReactor extends AbstractIOReactor {
         try {
             this.eventDispatch.connected(session);
         } catch (final CancelledKeyException ex) {
-            queueClosedSession(session);
+            throw ex;
         } catch (final RuntimeException ex) {
             handleRuntimeException(ex);
         }
@@ -262,7 +260,7 @@ public class BaseIOReactor extends AbstractIOReactor {
         try {
             this.eventDispatch.timeout(session);
         } catch (final CancelledKeyException ex) {
-            queueClosedSession(session);
+            throw ex;
         } catch (final RuntimeException ex) {
             handleRuntimeException(ex);
         }


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/DefaultConnectingIOReactor.java
=====================================
@@ -35,6 +35,9 @@ import java.net.UnknownHostException;
 import java.nio.channels.CancelledKeyException;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -207,7 +210,11 @@ public class DefaultConnectingIOReactor extends AbstractMultiworkerIOReactor
                 final int timeout = sessionRequest.getConnectTimeout();
                 if (timeout > 0) {
                     if (handle.getRequestTime() + timeout < now) {
-                        sessionRequest.timeout();
+                        try {
+                            sessionRequest.timeout();
+                        } finally {
+                            key.attach(null);
+                        }
                     }
                 }
             }
@@ -270,7 +277,25 @@ public class DefaultConnectingIOReactor extends AbstractMultiworkerIOReactor
                     sock.setReuseAddress(this.config.isSoReuseAddress());
                     sock.bind(request.getLocalAddress());
                 }
-                final boolean connected = socketChannel.connect(request.getRemoteAddress());
+
+                final SocketAddress targetAddress = request.getRemoteAddress();
+                // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect
+                // permissions only to this library
+                final boolean connected;
+                try {
+                    connected = AccessController.doPrivileged(
+                            new PrivilegedExceptionAction<Boolean>() {
+                                @Override
+                                public Boolean run() throws IOException {
+                                    return socketChannel.connect(targetAddress);
+                                };
+                            });
+                } catch (final PrivilegedActionException e) {
+                    Asserts.check(e.getCause() instanceof  IOException,
+                            "method contract violation only checked exceptions are wrapped: " + e.getCause());
+                    // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged
+                    throw (IOException) e.getCause();
+                }
                 if (connected) {
                     final ChannelEntry entry = new ChannelEntry(socketChannel, request);
                     addChannel(entry);


=====================================
httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/SessionRequestImpl.java
=====================================
@@ -31,9 +31,10 @@ import java.io.IOException;
 import java.net.SocketAddress;
 import java.nio.channels.Channel;
 import java.nio.channels.SelectionKey;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.apache.http.annotation.ThreadingBehavior;
 import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
 import org.apache.http.nio.reactor.IOSession;
 import org.apache.http.nio.reactor.SessionRequest;
 import org.apache.http.nio.reactor.SessionRequestCallback;
@@ -47,14 +48,15 @@ import org.apache.http.util.Args;
 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
 public class SessionRequestImpl implements SessionRequest {
 
-    private volatile boolean completed;
-    private volatile SelectionKey key;
-
     private final SocketAddress remoteAddress;
     private final SocketAddress localAddress;
     private final Object attachment;
     private final SessionRequestCallback callback;
+    private final AtomicBoolean completed;
+
+    private volatile SelectionKey key;
 
+    private volatile boolean terminated;
     private volatile int connectTimeout;
     private volatile IOSession session = null;
     private volatile IOException exception = null;
@@ -70,7 +72,7 @@ public class SessionRequestImpl implements SessionRequest {
         this.localAddress = localAddress;
         this.attachment = attachment;
         this.callback = callback;
-        this.connectTimeout = 0;
+        this.completed = new AtomicBoolean(false);
     }
 
     @Override
@@ -90,7 +92,11 @@ public class SessionRequestImpl implements SessionRequest {
 
     @Override
     public boolean isCompleted() {
-        return this.completed;
+        return this.completed.get();
+    }
+
+    boolean isTerminated() {
+        return this.terminated;
     }
 
     protected void setKey(final SelectionKey key) {
@@ -99,11 +105,11 @@ public class SessionRequestImpl implements SessionRequest {
 
     @Override
     public void waitFor() throws InterruptedException {
-        if (this.completed) {
+        if (this.completed.get()) {
             return;
         }
         synchronized (this) {
-            while (!this.completed) {
+            while (!this.completed.get()) {
                 wait();
             }
         }
@@ -125,16 +131,14 @@ public class SessionRequestImpl implements SessionRequest {
 
     public void completed(final IOSession session) {
         Args.notNull(session, "Session");
-        if (this.completed) {
-            return;
-        }
-        this.completed = true;
-        synchronized (this) {
-            this.session = session;
-            if (this.callback != null) {
-                this.callback.completed(this);
+        if (this.completed.compareAndSet(false, true)) {
+            synchronized (this) {
+                this.session = session;
+                if (this.callback != null) {
+                    this.callback.completed(this);
+                }
+                notifyAll();
             }
-            notifyAll();
         }
     }
 
@@ -142,45 +146,43 @@ public class SessionRequestImpl implements SessionRequest {
         if (exception == null) {
             return;
         }
-        if (this.completed) {
-            return;
-        }
-        this.completed = true;
-        final SelectionKey key = this.key;
-        if (key != null) {
-            key.cancel();
-            final Channel channel = key.channel();
-            try {
-                channel.close();
-            } catch (final IOException ignore) {}
-        }
-        synchronized (this) {
-            this.exception = exception;
-            if (this.callback != null) {
-                this.callback.failed(this);
+        if (this.completed.compareAndSet(false, true)) {
+            this.terminated = true;
+            final SelectionKey key = this.key;
+            if (key != null) {
+                key.cancel();
+                final Channel channel = key.channel();
+                try {
+                    channel.close();
+                } catch (final IOException ignore) {}
+            }
+            synchronized (this) {
+                this.exception = exception;
+                if (this.callback != null) {
+                    this.callback.failed(this);
+                }
+                notifyAll();
             }
-            notifyAll();
         }
     }
 
     public void timeout() {
-        if (this.completed) {
-            return;
-        }
-        this.completed = true;
-        final SelectionKey key = this.key;
-        if (key != null) {
-            key.cancel();
-            final Channel channel = key.channel();
-            if (channel.isOpen()) {
-                try {
-                    channel.close();
-                } catch (final IOException ignore) {}
+        if (this.completed.compareAndSet(false, true)) {
+            this.terminated = true;
+            final SelectionKey key = this.key;
+            if (key != null) {
+                key.cancel();
+                final Channel channel = key.channel();
+                if (channel.isOpen()) {
+                    try {
+                        channel.close();
+                    } catch (final IOException ignore) {}
+                }
             }
-        }
-        synchronized (this) {
-            if (this.callback != null) {
-                this.callback.timeout(this);
+            synchronized (this) {
+                if (this.callback != null) {
+                    this.callback.timeout(this);
+                }
             }
         }
     }
@@ -203,25 +205,24 @@ public class SessionRequestImpl implements SessionRequest {
 
     @Override
     public void cancel() {
-        if (this.completed) {
-            return;
-        }
-        this.completed = true;
-        final SelectionKey key = this.key;
-        if (key != null) {
-            key.cancel();
-            final Channel channel = key.channel();
-            if (channel.isOpen()) {
-                try {
-                    channel.close();
-                } catch (final IOException ignore) {}
+        if (this.completed.compareAndSet(false, true)) {
+            this.terminated = true;
+            final SelectionKey key = this.key;
+            if (key != null) {
+                key.cancel();
+                final Channel channel = key.channel();
+                if (channel.isOpen()) {
+                    try {
+                        channel.close();
+                    } catch (final IOException ignore) {}
+                }
             }
-        }
-        synchronized (this) {
-            if (this.callback != null) {
-                this.callback.cancelled(this);
+            synchronized (this) {
+                if (this.callback != null) {
+                    this.callback.cancelled(this);
+                }
+                notifyAll();
             }
-            notifyAll();
         }
     }
 


=====================================
httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java
=====================================
@@ -229,7 +229,11 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
             break;
         }
         if (this.handler != null) {
-            this.handler.initalize(this.sslEngine);
+            try {
+                this.handler.initalize(this.sslEngine);
+            } catch (final RuntimeException ex) {
+                throw convert(ex);
+            }
         }
         this.initialized = true;
         this.sslEngine.beginHandshake();
@@ -313,9 +317,11 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
 
                 // Perform operations
                 inEncryptedBuf.flip();
-                result = doUnwrap(inEncryptedBuf, inPlainBuf);
-                inEncryptedBuf.compact();
-
+                try {
+                    result = doUnwrap(inEncryptedBuf, inPlainBuf);
+                } finally {
+                    inEncryptedBuf.compact();
+                }
 
                 try {
                     if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
@@ -426,10 +432,14 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
         // Acquire buffer
         final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
 
+        final int bytesWritten;
         // Perform operation
         outEncryptedBuf.flip();
-        final int bytesWritten = this.session.channel().write(outEncryptedBuf);
-        outEncryptedBuf.compact();
+        try {
+            bytesWritten = this.session.channel().write(outEncryptedBuf);
+        } finally {
+            outEncryptedBuf.compact();
+        }
 
         // Release if empty
         if (outEncryptedBuf.position() == 0) {
@@ -466,10 +476,14 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
             final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
             final ByteBuffer inPlainBuf = this.inPlain.acquire();
 
+            final SSLEngineResult result;
             // Perform operations
             inEncryptedBuf.flip();
-            final SSLEngineResult result = doUnwrap(inEncryptedBuf, inPlainBuf);
-            inEncryptedBuf.compact();
+            try {
+                result = doUnwrap(inEncryptedBuf, inPlainBuf);
+            } finally {
+                inEncryptedBuf.compact();
+            }
 
             try {
                 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {


=====================================
httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestIdentityDecoder.java
=====================================
@@ -256,6 +256,74 @@ public class TestIdentityDecoder {
             CodecTestUtils.readFromFile(this.tmpfile));
     }
 
+    @Test
+    public void testDecodingFileWithLimit() throws Exception {
+        final ReadableByteChannel channel = new ReadableByteChannelMock(
+                new String[] {"stuff; more stuff; ", "a lot more stuff!"}, Consts.ASCII);
+
+        final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
+        final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
+        final IdentityDecoder decoder = new IdentityDecoder(
+                channel, inbuf, metrics);
+
+        final int i = inbuf.fill(channel);
+        Assert.assertEquals(19, i);
+
+        createTempFile();
+        final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
+        try {
+            final FileChannel fchannel = testfile.getChannel();
+            long pos = 0;
+
+            // transferred from buffer
+            long bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(1, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 2);
+            Assert.assertEquals(2, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 17);
+            Assert.assertEquals(16, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            // transferred from channel
+            bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(1, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(1, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 2);
+            Assert.assertEquals(2, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(3, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 15);
+            Assert.assertEquals(14, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(17, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(-1, bytesRead);
+            Assert.assertTrue(decoder.isCompleted());
+            Assert.assertEquals(17, metrics.getBytesTransferred());
+        } finally {
+            testfile.close();
+        }
+        Assert.assertEquals("stuff; more stuff; a lot more stuff!",
+                CodecTestUtils.readFromFile(this.tmpfile));
+    }
+
     @Test
     public void testWriteBeyondFileSize() throws Exception {
         final ReadableByteChannel channel = new ReadableByteChannelMock(


=====================================
httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestLengthDelimitedDecoder.java
=====================================
@@ -364,6 +364,74 @@ public class TestLengthDelimitedDecoder {
             CodecTestUtils.readFromFile(this.tmpfile));
     }
 
+    @Test
+    public void testDecodingFileWithLimit() throws Exception {
+        final ReadableByteChannel channel = new ReadableByteChannelMock(
+                new String[] {"stuff; more stuff; ", "a lot more stuff!!!"}, Consts.ASCII);
+
+        final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
+        final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
+        final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
+                channel, inbuf, metrics, 36);
+
+        final int i = inbuf.fill(channel);
+        Assert.assertEquals(19, i);
+
+        createTempFile();
+        final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
+        try {
+            final FileChannel fchannel = testfile.getChannel();
+            long pos = 0;
+
+            // transferred from buffer
+            long bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(1, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 2);
+            Assert.assertEquals(2, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 17);
+            Assert.assertEquals(16, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(0, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            // transferred from channel
+            bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(1, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(1, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 2);
+            Assert.assertEquals(2, bytesRead);
+            Assert.assertFalse(decoder.isCompleted());
+            Assert.assertEquals(3, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 15);
+            Assert.assertEquals(14, bytesRead);
+            Assert.assertTrue(decoder.isCompleted());
+            Assert.assertEquals(17, metrics.getBytesTransferred());
+            pos += bytesRead;
+
+            bytesRead = decoder.transfer(fchannel, pos, 1);
+            Assert.assertEquals(-1, bytesRead);
+            Assert.assertTrue(decoder.isCompleted());
+            Assert.assertEquals(17, metrics.getBytesTransferred());
+        } finally {
+            testfile.close();
+        }
+        Assert.assertEquals("stuff; more stuff; a lot more stuff!",
+                CodecTestUtils.readFromFile(this.tmpfile));
+    }
+
     @Test
     public void testWriteBeyondFileSize() throws Exception {
         final ReadableByteChannel channel = new ReadableByteChannelMock(


=====================================
httpcore-nio/src/test/java/org/apache/http/nio/integration/TestCustomSSL.java → httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java
=====================================
@@ -31,18 +31,22 @@ import java.io.IOException;
 import java.math.BigInteger;
 import java.net.InetSocketAddress;
 import java.net.URL;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
 
 import org.apache.http.HttpException;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
+import org.apache.http.config.ConnectionConfig;
 import org.apache.http.impl.nio.pool.BasicNIOConnFactory;
 import org.apache.http.message.BasicHttpRequest;
 import org.apache.http.nio.NHttpConnection;
@@ -58,30 +62,285 @@ import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpCoreContext;
 import org.apache.http.protocol.HttpRequestHandler;
 import org.apache.http.ssl.SSLContextBuilder;
-import org.junit.After;
+import org.apache.http.ssl.SSLContexts;
+import org.hamcrest.CoreMatchers;
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExternalResource;
 
-public class TestCustomSSL {
+public class TestTLSIntegration {
 
     private final static long RESULT_TIMEOUT_SEC = 30;
 
-    protected HttpServerNio server;
-    protected HttpClientNio client;
+    private HttpServerNio server;
 
-    @After
-    public void shutDownClient() throws Exception {
-        if (this.client != null) {
-            this.client.shutdown();
-            this.client = null;
+    @Rule
+    public ExternalResource serverResource = new ExternalResource() {
+
+        @Override
+        protected void after() {
+            if (server != null) {
+                try {
+                    server.shutdown();
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    private HttpClientNio client;
+
+    @Rule
+    public ExternalResource clientResource = new ExternalResource() {
+
+        @Override
+        protected void after() {
+            if (client != null) {
+                try {
+                    client.shutdown();
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    private static SSLContext createServerSSLContext() throws Exception {
+        final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
+                .build();
+
+    }
+
+    private static SSLContext createClientSSLContext() throws Exception {
+        final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .build();
+    }
+
+    @Test
+    public void testTLSSuccess() throws Exception {
+        server = new HttpServerNio();
+        server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), null));
+        server.setTimeout(5000);
+        server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        server.start();
+
+        final AtomicReference<SSLSession> sslSessionRef = new AtomicReference<SSLSession>(null);
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+                sslSessionRef.set(sslSession);
+            }
+
+        }, ConnectionConfig.DEFAULT));
+        client.setTimeout(5000);
+        client.start();
+
+        final ListenerEndpoint endpoint = server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+
+        final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
+        final Future<HttpResponse> future = client.execute(target, request);
+        final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+        Assert.assertThat(response, CoreMatchers.notNullValue());
+        Assert.assertThat(response.getStatusLine().getStatusCode(), CoreMatchers.equalTo(200));
+
+        final SSLSession sslSession = sslSessionRef.getAndSet(null);
+        Assert.assertThat(sslSession.getPeerPrincipal().getName(),
+                CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation"));
+    }
+
+    @Test
+    public void testTLSTrustFailure() throws Exception {
+        server = new HttpServerNio();
+        server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), null));
+        server.setTimeout(5000);
+        server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        server.start();
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
+        client.setTimeout(5000);
+        client.start();
+
+        final ListenerEndpoint endpoint = server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+
+        final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
+        final Future<HttpResponse> future = client.execute(target, request);
+        try {
+            future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause();
+            Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(SSLHandshakeException.class));
+        }
+    }
+
+    @Test
+    public void testTLSClientAuthFailure() throws Exception {
+        server = new HttpServerNio();
+        server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+                sslEngine.setNeedClientAuth(true);
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+            }
+
+        }));
+        server.setTimeout(5000);
+        server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        server.start();
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), null, ConnectionConfig.DEFAULT));
+        client.setTimeout(5000);
+        client.start();
+
+        final ListenerEndpoint endpoint = server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+
+        final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
+        final Future<HttpResponse> future = client.execute(target, request);
+        try {
+            future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause();
+            Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
         }
     }
 
-    @After
-    public void shutDownServer() throws Exception {
-        if (this.server != null) {
-            this.server.shutdown();
-            this.server = null;
+    @Test
+    public void testTLSProtocolMismatch() throws Exception {
+        server = new HttpServerNio();
+        server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+                sslEngine.setEnabledProtocols(new String[]{"TLSv1.2"});
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+            }
+
+        }));
+        server.setTimeout(5000);
+        server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        server.start();
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+                sslEngine.setEnabledProtocols(new String[]{"SSLv3"});
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+            }
+
+        }, ConnectionConfig.DEFAULT));
+        client.setTimeout(5000);
+        client.start();
+
+        final ListenerEndpoint endpoint = server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+
+        final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
+        final Future<HttpResponse> future = client.execute(target, request);
+        try {
+            future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause();
+            Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
+        }
+    }
+
+    @Test
+    public void testTLSCipherMismatch() throws Exception {
+        server = new HttpServerNio();
+        server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+                sslEngine.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+            }
+
+        }));
+        server.setTimeout(5000);
+        server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        server.start();
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
+
+            @Override
+            public void initalize(final SSLEngine sslEngine) throws SSLException {
+                sslEngine.setEnabledCipherSuites(new String[]{"SSL_RSA_EXPORT_WITH_RC4_40_MD5"});
+            }
+
+            @Override
+            public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
+            }
+
+        }, ConnectionConfig.DEFAULT));
+        client.setTimeout(5000);
+        client.start();
+
+        final ListenerEndpoint endpoint = server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+
+        final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
+        final Future<HttpResponse> future = client.execute(target, request);
+        try {
+            future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause();
+            Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
         }
     }
 
@@ -119,26 +378,14 @@ public class TestCustomSSL {
 
         };
 
-        final URL keyStoreURL = getClass().getResource("/test.keystore");
-        final String storePassword = "nopassword";
-        final SSLContext serverSSLContext = SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
-                .build();
         this.server = new HttpServerNio();
-        this.server.setConnectionFactory(new ServerConnectionFactory(serverSSLContext, sslSetupHandler));
+        this.server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), sslSetupHandler));
         this.server.setTimeout(5000);
-
-        final SSLContext clientSSLContext = SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .build();
-
-        this.client = new HttpClientNio(new BasicNIOConnFactory(new ClientConnectionFactory(clientSSLContext), null));
-        this.client.setTimeout(5000);
-
         this.server.registerHandler("*", new BasicAsyncRequestHandler(requestHandler));
-
         this.server.start();
+
+        this.client = new HttpClientNio(new BasicNIOConnFactory(new ClientConnectionFactory(createClientSSLContext()), null));
+        this.client.setTimeout(5000);
         this.client.start();
 
         final ListenerEndpoint endpoint = this.server.getListenerEndpoint();


=====================================
httpcore-osgi/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-core</artifactId>
-    <version>4.4.11</version>
+    <version>4.4.12</version>
   </parent>
   <artifactId>httpcore-osgi</artifactId>
   <name>Apache HttpCore OSGi bundle</name>
@@ -127,7 +127,6 @@
       </plugin>
       <plugin>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>${hc.project-info.version}</version>
         <inherited>false</inherited>
         <reportSets>
           <reportSet>


=====================================
httpcore/pom.xml
=====================================
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-core</artifactId>
-    <version>4.4.11</version>
+    <version>4.4.12</version>
   </parent>
   <artifactId>httpcore</artifactId>
   <name>Apache HttpCore</name>
@@ -112,7 +112,6 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -132,7 +131,6 @@
 
       <plugin>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>${hc.project-info.version}</version>
         <inherited>false</inherited>
         <reportSets>
           <reportSet>
@@ -147,12 +145,10 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
-        <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
-        <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>


=====================================
httpcore/src/main/java/org/apache/http/config/SocketConfig.java
=====================================
@@ -129,7 +129,7 @@ public class SocketConfig implements Cloneable {
      * Determines the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter
      * for newly created sockets.
      * <p>
-     * Default: {@code false}
+     * Default: {@code true}
      * </p>
      *
      * @return the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter.


=====================================
httpcore/src/main/java/org/apache/http/entity/ContentType.java
=====================================
@@ -77,6 +77,8 @@ public final class ContentType implements Serializable {
             "application/json", Consts.UTF_8);
     public static final ContentType APPLICATION_OCTET_STREAM = create(
             "application/octet-stream", (Charset) null);
+    public static final ContentType APPLICATION_SOAP_XML = create(
+            "application/soap+xml", Consts.UTF_8);
     public static final ContentType APPLICATION_SVG_XML = create(
             "application/svg+xml", Consts.ISO_8859_1);
     public static final ContentType APPLICATION_XHTML_XML = create(


=====================================
httpcore/src/main/java/org/apache/http/impl/pool/BasicConnFactory.java
=====================================
@@ -29,6 +29,9 @@ package org.apache.http.impl.pool;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 
 import javax.net.SocketFactory;
 import javax.net.ssl.SSLSocketFactory;
@@ -36,8 +39,8 @@ import javax.net.ssl.SSLSocketFactory;
 import org.apache.http.HttpClientConnection;
 import org.apache.http.HttpConnectionFactory;
 import org.apache.http.HttpHost;
-import org.apache.http.annotation.ThreadingBehavior;
 import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
 import org.apache.http.config.ConnectionConfig;
 import org.apache.http.config.SocketConfig;
 import org.apache.http.impl.DefaultBHttpClientConnection;
@@ -47,6 +50,7 @@ import org.apache.http.params.HttpParamConfig;
 import org.apache.http.params.HttpParams;
 import org.apache.http.pool.ConnFactory;
 import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
 
 /**
  * A very basic {@link ConnFactory} implementation that creates
@@ -145,15 +149,14 @@ public class BasicConnFactory implements ConnFactory<HttpHost, HttpClientConnect
     @Override
     public HttpClientConnection create(final HttpHost host) throws IOException {
         final String scheme = host.getSchemeName();
-        Socket socket = null;
+        final Socket socket;
         if ("http".equalsIgnoreCase(scheme)) {
             socket = this.plainfactory != null ? this.plainfactory.createSocket() :
                     new Socket();
-        } if ("https".equalsIgnoreCase(scheme)) {
+        } else if ("https".equalsIgnoreCase(scheme)) {
             socket = (this.sslfactory != null ? this.sslfactory :
                     SSLSocketFactory.getDefault()).createSocket();
-        }
-        if (socket == null) {
+        } else {
             throw new IOException(scheme + " scheme is not supported");
         }
         final String hostname = host.getHostName();
@@ -178,7 +181,23 @@ public class BasicConnFactory implements ConnFactory<HttpHost, HttpClientConnect
             socket.setSoLinger(true, linger);
         }
         socket.setKeepAlive(this.sconfig.isSoKeepAlive());
-        socket.connect(new InetSocketAddress(hostname, port), this.connectTimeout);
+        // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions
+        // only to this library
+        final InetSocketAddress address = new InetSocketAddress(hostname, port);
+        try {
+            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                @Override
+                public Object run() throws IOException {
+                    socket.connect(address, BasicConnFactory.this.connectTimeout);
+                    return null;
+                }
+            });
+        } catch (final PrivilegedActionException e) {
+            Asserts.check(e.getCause() instanceof IOException,
+                    "method contract violation only checked exceptions are wrapped: " + e.getCause());
+            // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged
+            throw (IOException) e.getCause();
+        }
         return this.connFactory.createConnection(socket);
     }
 


=====================================
httpcore/src/main/java/org/apache/http/message/HeaderGroup.java
=====================================
@@ -49,7 +49,7 @@ public class HeaderGroup implements Cloneable, Serializable {
 
     private static final long serialVersionUID = 2608834160639271617L;
 
-    private final Header[] EMPTY = new Header[] {};
+    private static final Header[] EMPTY = new Header[] {};
 
     /** The list of headers for this group, in the order in which they were added */
     private final List<Header> headers;


=====================================
httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
=====================================
@@ -34,6 +34,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -177,6 +178,10 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
         return pool;
     }
 
+    private static Exception operationAborted() {
+        return new CancellationException("Operation aborted");
+    }
+
     /**
      * {@inheritDoc}
      * <p>
@@ -198,8 +203,8 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
 
             @Override
             public boolean cancel(final boolean mayInterruptIfRunning) {
-                if (cancelled.compareAndSet(false, true)) {
-                    done.set(true);
+                if (done.compareAndSet(false, true)) {
+                    cancelled.set(true);
                     lock.lock();
                     try {
                         condition.signalAll();
@@ -235,13 +240,16 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
 
             @Override
             public E get(final long timeout, final TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
-                final E entry = entryRef.get();
-                if (entry != null) {
-                    return entry;
-                }
-                synchronized (this) {
-                    try {
-                        for (;;) {
+                for (;;) {
+                    synchronized (this) {
+                        try {
+                            final E entry = entryRef.get();
+                            if (entry != null) {
+                                return entry;
+                            }
+                            if (done.get()) {
+                                throw new ExecutionException(operationAborted());
+                            }
                             final E leasedEntry = getPoolEntryBlocking(route, state, timeout, timeUnit, this);
                             if (validateAfterInactivity > 0)  {
                                 if (leasedEntry.getUpdated() + validateAfterInactivity <= System.currentTimeMillis()) {
@@ -252,20 +260,26 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
                                     }
                                 }
                             }
-                            entryRef.set(leasedEntry);
-                            done.set(true);
-                            onLease(leasedEntry);
-                            if (callback != null) {
-                                callback.completed(leasedEntry);
+                            if (done.compareAndSet(false, true)) {
+                                entryRef.set(leasedEntry);
+                                done.set(true);
+                                onLease(leasedEntry);
+                                if (callback != null) {
+                                    callback.completed(leasedEntry);
+                                }
+                                return leasedEntry;
+                            } else {
+                                release(leasedEntry, true);
+                                throw new ExecutionException(operationAborted());
                             }
-                            return leasedEntry;
-                        }
-                    } catch (final IOException ex) {
-                        done.set(true);
-                        if (callback != null) {
-                            callback.failed(ex);
+                        } catch (final IOException ex) {
+                            if (done.compareAndSet(false, true)) {
+                                if (callback != null) {
+                                    callback.failed(ex);
+                                }
+                            }
+                            throw new ExecutionException(ex);
                         }
-                        throw new ExecutionException(ex);
                     }
                 }
             }
@@ -296,7 +310,7 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
     private E getPoolEntryBlocking(
             final T route, final Object state,
             final long timeout, final TimeUnit timeUnit,
-            final Future<E> future) throws IOException, InterruptedException, TimeoutException {
+            final Future<E> future) throws IOException, InterruptedException, ExecutionException, TimeoutException {
 
         Date deadline = null;
         if (timeout > 0) {
@@ -308,6 +322,9 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
             E entry;
             for (;;) {
                 Asserts.check(!this.isShutDown, "Connection pool shut down");
+                if (future.isCancelled()) {
+                    throw new ExecutionException(operationAborted());
+                }
                 for (;;) {
                     entry = pool.getFree(state);
                     if (entry == null) {
@@ -368,9 +385,6 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
 
                 boolean success = false;
                 try {
-                    if (future.isCancelled()) {
-                        throw new InterruptedException("Operation interrupted");
-                    }
                     pool.queue(future);
                     this.pending.add(future);
                     if (deadline != null) {
@@ -380,7 +394,7 @@ public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
                         success = true;
                     }
                     if (future.isCancelled()) {
-                        throw new InterruptedException("Operation interrupted");
+                        throw new ExecutionException(operationAborted());
                     }
                 } finally {
                     // In case of 'success', we were woken up by the


=====================================
httpcore/src/main/java/org/apache/http/util/ByteArrayBuffer.java
=====================================
@@ -135,8 +135,14 @@ public final class ByteArrayBuffer implements Serializable {
         if (newlen > this.buffer.length) {
             expand(newlen);
         }
+
         for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
-            this.buffer[i2] = (byte) b[i1];
+            if ((b[i1] >= 0x20 && b[i1] <= 0x7E) || // Visible ASCII
+                (b[i1] >= 0xA0 && b[i1] <= 0xFF)) { // Visible ISO-8859-1
+                this.buffer[i2] = (byte) b[i1];
+            } else {
+                this.buffer[i2] = '?';
+            }
         }
         this.len = newlen;
     }


=====================================
httpcore/src/test/java/org/apache/http/util/TestByteArrayBuffer.java
=====================================
@@ -312,4 +312,44 @@ public class TestByteArrayBuffer {
         Assert.assertEquals(3, data[2]);
     }
 
+    @Test
+    public void testControlCharFiltering() throws Exception {
+        final char[] chars = new char[256];
+        for (char i = 0; i < 256; i++) {
+            chars[i] = i;
+        }
+
+        final byte[] bytes = asByteArray(chars);
+
+        Assert.assertEquals(
+            "????????????????????????????????"
+                + " !\"#$%&'()*+,-./0123456789:;<=>?"
+                + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+                + "`abcdefghijklmnopqrstuvwxyz"
+                + "{|}~???????????????????????"
+                + "??????????\u00A0¡¢£¤¥¦§¨©ª«¬\u00AD®¯"
+                + "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"
+                + "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîï"
+                + "ðñòóôõö÷øùúûüýþÿ",
+            new String(bytes, "ISO-8859-1"));
+    }
+
+    @Test
+    public void testUnicodeFiltering() throws Exception {
+        // Various languages
+        Assert.assertEquals("?????", new String(asByteArray("буквы".toCharArray()), "ISO-8859-1"));
+        Assert.assertEquals("????", new String(asByteArray("四字熟語".toCharArray()), "ISO-8859-1"));
+
+        // Unicode snowman
+        Assert.assertEquals("?", new String(asByteArray("☃".toCharArray()), "ISO-8859-1"));
+
+        // Emoji (surrogate pair)
+        Assert.assertEquals("??", new String(asByteArray("\uD83D\uDE00".toCharArray()), "ISO-8859-1"));
+    }
+
+    private static byte[] asByteArray(final char[] chars) {
+        final ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(chars.length);
+        byteArrayBuffer.append(chars, 0, chars.length);
+        return byteArrayBuffer.toByteArray();
+    }
 }


=====================================
pom.xml
=====================================
@@ -33,7 +33,7 @@
   <modelVersion>4.0.0</modelVersion>
   <artifactId>httpcomponents-core</artifactId>
   <name>Apache HttpComponents Core</name>
-  <version>4.4.11</version>
+  <version>4.4.12</version>
   <description>Apache HttpComponents Core is a library of components for building HTTP enabled services</description>
   <url>http://hc.apache.org/httpcomponents-core-ga</url>
   <inceptionYear>2005</inceptionYear>
@@ -61,7 +61,7 @@
     <connection>scm:git:https://git-wip-us.apache.org/repos/asf/httpcomponents-core.git</connection>
     <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/httpcomponents-core.git</developerConnection>
     <url>https://github.com/apache/httpcomponents-core/tree/${project.scm.tag}</url>
-    <tag>4.4.11</tag>
+    <tag>4.4.12</tag>
   </scm>
 
   <modules>
@@ -325,14 +325,15 @@
 
       <plugin>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>${hc.project-info.version}</version>
         <inherited>false</inherited>
         <reportSets>
           <reportSet>
             <reports>
+              <report>dependency-info</report>
               <report>dependency-management</report>
-              <report>issue-tracking</report>
-              <report>license</report>
+              <report>issue-management</report>
+              <report>licenses</report>
+              <report>mailing-lists</report>
               <report>scm</report>
               <report>summary</report>
             </reports>



View it on GitLab: https://salsa.debian.org/java-team/httpcomponents-core/commit/acce30fec8ea58a642a51cb00b83fec339eb4630

-- 
View it on GitLab: https://salsa.debian.org/java-team/httpcomponents-core/commit/acce30fec8ea58a642a51cb00b83fec339eb4630
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20191005/54187cc6/attachment.html>


More information about the pkg-java-commits mailing list