[Git][java-team/httpcomponents-core][master] 7 commits: New upstream version 4.4.12
Emmanuel Bourg
gitlab at salsa.debian.org
Sat Oct 5 09:07:00 BST 2019
Emmanuel Bourg pushed to branch master at Debian Java Maintainers / httpcomponents-core
Commits:
acce30fe by Emmanuel Bourg at 2019-10-05T07:58:57Z
New upstream version 4.4.12
- - - - -
db7acdea by Emmanuel Bourg at 2019-10-05T07:59:02Z
Update upstream source from tag 'upstream/4.4.12'
Update to upstream version '4.4.12'
with Debian dir 0420d4050d2c955425ff98e5a467cd9da497ef75
- - - - -
7d0e0f89 by Emmanuel Bourg at 2019-10-05T08:00:33Z
New upstream release (4.4.12)
- - - - -
2c663ae5 by Emmanuel Bourg at 2019-10-05T08:00:46Z
Standards-Version updated to 4.4.1
- - - - -
e1554a98 by Emmanuel Bourg at 2019-10-05T08:02:26Z
Removed the unused ${java:Depends} variable in debian/control
- - - - -
248728c7 by Emmanuel Bourg at 2019-10-05T08:05:36Z
Use a secure URI in debian/watch
- - - - -
395643c8 by Emmanuel Bourg at 2019-10-05T08:05:46Z
Upload to unstable
- - - - -
26 changed files:
- RELEASE_NOTES.txt
- debian/changelog
- debian/control
- debian/watch
- 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
-------------------
=====================================
debian/changelog
=====================================
@@ -1,3 +1,12 @@
+httpcomponents-core (4.4.12-1) unstable; urgency=medium
+
+ * New upstream release
+ * Removed the unused ${java:Depends} variable in debian/control
+ * Standards-Version updated to 4.4.1
+ * Use a secure URI in debian/watch
+
+ -- Emmanuel Bourg <ebourg at apache.org> Sat, 05 Oct 2019 10:05:40 +0200
+
httpcomponents-core (4.4.11-1) unstable; urgency=medium
* New upstream release
=====================================
debian/control
=====================================
@@ -10,14 +10,14 @@ Build-Depends:
libmaven-antrun-plugin-java,
libmaven-bundle-plugin-java,
maven-debian-helper
-Standards-Version: 4.3.0
+Standards-Version: 4.4.1
Vcs-Git: https://salsa.debian.org/java-team/httpcomponents-core.git
Vcs-Browser: https://salsa.debian.org/java-team/httpcomponents-core
Homepage: http://hc.apache.org/httpcomponents-core-ga/index.html
Package: libhttpcore-java
Architecture: all
-Depends: ${java:Depends}, ${misc:Depends}
+Depends: ${misc:Depends}
Provides: libhttpcore-nio-java
Description: set of low level HTTP transport components for Java
HttpCore is a set of low level HTTP transport components that can be used
=====================================
debian/watch
=====================================
@@ -1,2 +1,2 @@
version=3
-http://www.apache.org/dist/httpcomponents/httpcore/source/httpcomponents-core-(\d+[^a-zA-Z]*)-src\.tar\.gz
+https://www.apache.org/dist/httpcomponents/httpcore/source/httpcomponents-core-(\d+[^a-zA-Z]*)-src\.tar\.gz
=====================================
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/compare/5322aa156738934cc15fca37f5d504c75fb1ab8f...395643c88151ec6dcc2497f4a48652555f3b3bf8
--
View it on GitLab: https://salsa.debian.org/java-team/httpcomponents-core/compare/5322aa156738934cc15fca37f5d504c75fb1ab8f...395643c88151ec6dcc2497f4a48652555f3b3bf8
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/c484805a/attachment.html>
More information about the pkg-java-commits
mailing list