diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 742c17f49c..10baa6be79 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -211,11 +211,15 @@ public final class Unpooled { * Creates a new buffer which wraps the specified buffer's readable bytes. * A modification on the specified buffer's content will be visible to the * returned buffer. + * @param buffer The buffer to wrap. Reference count ownership of this variable is transfered to this method. + * @return The readable portion of the {@code buffer}, or an empty buffer if there is no readable portion. + * The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(ByteBuf buffer) { if (buffer.isReadable()) { return buffer.slice(); } else { + buffer.release(); return EMPTY_BUFFER; } } @@ -233,6 +237,8 @@ public final class Unpooled { * Creates a new big-endian composite buffer which wraps the readable bytes of the * specified buffers without copying them. A modification on the content * of the specified buffers will be visible to the returned buffer. + * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method. + * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(ByteBuf... buffers) { return wrappedBuffer(16, buffers); @@ -285,20 +291,29 @@ public final class Unpooled { * Creates a new big-endian composite buffer which wraps the readable bytes of the * specified buffers without copying them. A modification on the content * of the specified buffers will be visible to the returned buffer. + * @param maxNumComponents Advisement as to how many independent buffers are allowed to exist before + * consolidation occurs. + * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method. + * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) { switch (buffers.length) { case 0: break; case 1: - if (buffers[0].isReadable()) { - return wrappedBuffer(buffers[0].order(BIG_ENDIAN)); + ByteBuf buffer = buffers[0]; + if (buffer.isReadable()) { + return wrappedBuffer(buffer.order(BIG_ENDIAN)); + } else { + buffer.release(); } break; default: for (ByteBuf b: buffers) { if (b.isReadable()) { return new CompositeByteBuf(ALLOC, false, maxNumComponents, buffers); + } else { + b.release(); } } } diff --git a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java b/buffer/src/test/java/io/netty/buffer/UnpooledTest.java index 0b2e5dc816..d0dfc1ed29 100644 --- a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java +++ b/buffer/src/test/java/io/netty/buffer/UnpooledTest.java @@ -283,6 +283,42 @@ public class UnpooledTest { ByteBuffer.wrap(new byte[] { 3 })))); } + @Test + public void testSingleWrappedByteBufReleased() { + ByteBuf buf = buffer(12).writeByte(0); + ByteBuf wrapped = wrappedBuffer(buf); + assertTrue(wrapped.release()); + assertEquals(0, buf.refCnt()); + } + + @Test + public void testSingleUnReadableWrappedByteBufReleased() { + ByteBuf buf = buffer(12); + ByteBuf wrapped = wrappedBuffer(buf); + assertFalse(wrapped.release()); // EMPTY_BUFFER cannot be released + assertEquals(0, buf.refCnt()); + } + + @Test + public void testMultiByteBufReleased() { + ByteBuf buf1 = buffer(12).writeByte(0); + ByteBuf buf2 = buffer(12).writeByte(0); + ByteBuf wrapped = wrappedBuffer(16, buf1, buf2); + assertTrue(wrapped.release()); + assertEquals(0, buf1.refCnt()); + assertEquals(0, buf2.refCnt()); + } + + @Test + public void testMultiUnReadableByteBufReleased() { + ByteBuf buf1 = buffer(12); + ByteBuf buf2 = buffer(12); + ByteBuf wrapped = wrappedBuffer(16, buf1, buf2); + assertFalse(wrapped.release()); // EMPTY_BUFFER cannot be released + assertEquals(0, buf1.refCnt()); + assertEquals(0, buf2.refCnt()); + } + @Test public void testCopiedBuffer() { assertEquals(16, copiedBuffer(ByteBuffer.allocateDirect(16)).capacity()); diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java index 8eeeeee687..abfe9f25f3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java @@ -486,9 +486,18 @@ public abstract class OpenSslContext extends SslContext { ByteBuf buffer = Unpooled.directBuffer(); try { buffer.writeBytes(BEGIN_PRIVATE_KEY); - ByteBuf encoded = Base64.encode(Unpooled.wrappedBuffer(key.getEncoded()), true); - buffer.writeBytes(encoded); - encoded.release(); + ByteBuf wrappedBuf = Unpooled.wrappedBuffer(key.getEncoded()); + final ByteBuf encodedBuf; + try { + encodedBuf = Base64.encode(wrappedBuf, true); + try { + buffer.writeBytes(encodedBuf); + } finally { + encodedBuf.release(); + } + } finally { + wrappedBuf.release(); + } buffer.writeBytes(END_PRIVATE_KEY); return newBIO(buffer); } finally { @@ -508,9 +517,17 @@ public abstract class OpenSslContext extends SslContext { try { for (X509Certificate cert: certChain) { buffer.writeBytes(BEGIN_CERT); - ByteBuf encoded = Base64.encode(Unpooled.wrappedBuffer(cert.getEncoded()), true); - buffer.writeBytes(encoded); - encoded.release(); + ByteBuf wrappedBuf = Unpooled.wrappedBuffer(cert.getEncoded()); + try { + ByteBuf encodedBuf = Base64.encode(wrappedBuf, true); + try { + buffer.writeBytes(encodedBuf); + } finally { + encodedBuf.release(); + } + } finally { + wrappedBuf.release(); + } buffer.writeBytes(END_CERT); } return newBIO(buffer); diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java index f82b10af78..71f72f14a3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java @@ -219,10 +219,21 @@ public final class SelfSignedCertificate { static String[] newSelfSignedCertificate( String fqdn, PrivateKey key, X509Certificate cert) throws IOException, CertificateEncodingException { // Encode the private key into a file. - ByteBuf enc = Base64.encode(Unpooled.wrappedBuffer(key.getEncoded()), true); - String keyText = "-----BEGIN PRIVATE KEY-----\n" + enc.toString(CharsetUtil.US_ASCII) + - "\n-----END PRIVATE KEY-----\n"; - enc.release(); + ByteBuf wrappedBuf = Unpooled.wrappedBuffer(key.getEncoded()); + ByteBuf encodedBuf; + final String keyText; + try { + encodedBuf = Base64.encode(wrappedBuf, true); + try { + keyText = "-----BEGIN PRIVATE KEY-----\n" + + encodedBuf.toString(CharsetUtil.US_ASCII) + + "\n-----END PRIVATE KEY-----\n"; + } finally { + encodedBuf.release(); + } + } finally { + wrappedBuf.release(); + } File keyFile = File.createTempFile("keyutil_" + fqdn + '_', ".key"); keyFile.deleteOnExit(); @@ -239,12 +250,21 @@ public final class SelfSignedCertificate { } } - ByteBuf encoded = Base64.encode(Unpooled.wrappedBuffer(cert.getEncoded()), true); - // Encode the certificate into a CRT file. - String certText = "-----BEGIN CERTIFICATE-----\n" + - encoded.toString(CharsetUtil.US_ASCII) + - "\n-----END CERTIFICATE-----\n"; - encoded.release(); + wrappedBuf = Unpooled.wrappedBuffer(cert.getEncoded()); + final String certText; + try { + encodedBuf = Base64.encode(wrappedBuf, true); + try { + // Encode the certificate into a CRT file. + certText = "-----BEGIN CERTIFICATE-----\n" + + encodedBuf.toString(CharsetUtil.US_ASCII) + + "\n-----END CERTIFICATE-----\n"; + } finally { + encodedBuf.release(); + } + } finally { + wrappedBuf.release(); + } File certFile = File.createTempFile("keyutil_" + fqdn + '_', ".crt"); certFile.deleteOnExit();