Reduce memory usage of SslHandler when OpenSslEngine is in use

Motivation:

JDK's SSLEngine.wrap() requires the output buffer to be always as large as MAX_ENCRYPTED_PACKET_LENGTH even if the input buffer contains small number of bytes.  Our OpenSslEngine implementation does not have such wasteful behaviot.

Modifications:

If the current SSLEngine is OpenSslEngine, allocate as much as only needed.

Result:

Less peak memory usage.
This commit is contained in:
Trustin Lee 2014-05-18 05:09:22 +09:00
parent ef59c4ce61
commit ae61b12b9e
2 changed files with 31 additions and 4 deletions

View File

@ -66,6 +66,8 @@ public final class OpenSslEngine extends SSLEngine {
// Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256) // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256; static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER = private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed"); AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");

View File

@ -180,6 +180,14 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
private final SSLEngine engine; private final SSLEngine engine;
private final int maxPacketBufferSize; private final int maxPacketBufferSize;
private final Executor delegatedTaskExecutor; private final Executor delegatedTaskExecutor;
/**
* {@code true} if and only if {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} requires the output buffer
* to be always as large as {@link #maxPacketBufferSize} even if the input buffer contains small amount of data.
* <p>
* If this flag is {@code false}, we allocate a smaller output buffer.
* </p>
*/
private final boolean needsLargeOutNetBuf;
private final boolean startTls; private final boolean startTls;
private boolean sentFirstMessage; private boolean sentFirstMessage;
@ -243,6 +251,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
this.delegatedTaskExecutor = delegatedTaskExecutor; this.delegatedTaskExecutor = delegatedTaskExecutor;
this.startTls = startTls; this.startTls = startTls;
maxPacketBufferSize = engine.getSession().getPacketBufferSize(); maxPacketBufferSize = engine.getSession().getPacketBufferSize();
needsLargeOutNetBuf = !(engine instanceof OpenSslEngine);
} }
public long getHandshakeTimeoutMillis() { public long getHandshakeTimeoutMillis() {
@ -426,16 +435,18 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
if (pending == null) { if (pending == null) {
break; break;
} }
if (out == null) {
out = allocate(ctx, maxPacketBufferSize);
}
if (!(pending.msg() instanceof ByteBuf)) { if (!(pending.msg() instanceof ByteBuf)) {
ctx.write(pending.msg(), (ChannelPromise) pending.recycleAndGet()); ctx.write(pending.msg(), (ChannelPromise) pending.recycleAndGet());
pendingUnencryptedWrites.remove(); pendingUnencryptedWrites.remove();
continue; continue;
} }
ByteBuf buf = (ByteBuf) pending.msg(); ByteBuf buf = (ByteBuf) pending.msg();
if (out == null) {
out = allocateOutNetBuf(ctx, buf.readableBytes());
}
SSLEngineResult result = wrap(engine, buf, out); SSLEngineResult result = wrap(engine, buf, out);
if (!buf.isReadable()) { if (!buf.isReadable()) {
@ -513,7 +524,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
try { try {
for (;;) { for (;;) {
if (out == null) { if (out == null) {
out = allocate(ctx, maxPacketBufferSize); out = allocateOutNetBuf(ctx, 0);
} }
SSLEngineResult result = wrap(engine, Unpooled.EMPTY_BUFFER, out); SSLEngineResult result = wrap(engine, Unpooled.EMPTY_BUFFER, out);
@ -1228,6 +1239,20 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
} }
} }
/**
* Allocates an outbound network buffer for {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} which can encrypt
* the specified amount of pending bytes.
*/
private ByteBuf allocateOutNetBuf(ChannelHandlerContext ctx, int pendingBytes) {
if (needsLargeOutNetBuf) {
return allocate(ctx, maxPacketBufferSize);
} else {
return allocate(ctx, Math.min(
pendingBytes + OpenSslEngine.MAX_ENCRYPTION_OVERHEAD_LENGTH,
maxPacketBufferSize));
}
}
private final class LazyChannelPromise extends DefaultPromise<Channel> { private final class LazyChannelPromise extends DefaultPromise<Channel> {
@Override @Override