Merge pull request #1929 from wgallagher/netty3spdybuf

avoid holding onto temporary buffers in SpdyFrameCodec
This commit is contained in:
Jeff Pinner 2013-10-17 11:45:37 -07:00
commit f739579339
4 changed files with 80 additions and 66 deletions

View File

@ -20,6 +20,7 @@ import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
@ -89,17 +90,6 @@ public class SpdyFrameDecoder extends FrameDecoder {
state = State.READ_COMMON_HEADER;
}
@Override
protected Object decodeLast(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
throws Exception {
try {
return decode(ctx, channel, buffer);
} finally {
headerBlockDecoder.end();
}
}
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
@ -521,6 +511,15 @@ public class SpdyFrameDecoder extends FrameDecoder {
}
}
@Override
protected void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
try {
super.cleanup(ctx, e);
} finally {
headerBlockDecoder.end();
}
}
private static void fireInvalidFrameException(ChannelHandlerContext ctx) {
Channels.fireExceptionCaught(ctx, INVALID_FRAME);
}

View File

@ -16,19 +16,19 @@
package org.jboss.netty.handler.codec.spdy;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
private static final int LENGTH_FIELD_SIZE = 4;
private final int version;
private final int maxHeaderSize;
private final int lengthFieldSize;
// Header block decoding fields
private int headerSize;
private int numHeaders;
private int numHeaders = -1;
public SpdyHeaderBlockRawDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
if (spdyVersion == null) {
@ -37,19 +37,11 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
this.version = spdyVersion.getVersion();
this.maxHeaderSize = maxHeaderSize;
lengthFieldSize = version < 3 ? 2 : 4;
reset();
}
private int readLengthField(ChannelBuffer buffer) {
int length;
if (version < 3) {
length = getUnsignedShort(buffer, buffer.readerIndex());
buffer.skipBytes(2);
} else {
length = getSignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(4);
}
int length = getSignedInt(buffer, buffer.readerIndex());
buffer.skipBytes(LENGTH_FIELD_SIZE);
return length;
}
@ -64,7 +56,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
if (numHeaders == -1) {
// Read number of Name/Value pairs
if (encoded.readableBytes() < lengthFieldSize) {
if (encoded.readableBytes() < LENGTH_FIELD_SIZE) {
return;
}
numHeaders = readLengthField(encoded);
@ -79,7 +71,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
encoded.markReaderIndex();
// Try to read length of name
if (encoded.readableBytes() < lengthFieldSize) {
if (encoded.readableBytes() < LENGTH_FIELD_SIZE) {
encoded.resetReaderIndex();
return;
}
@ -112,7 +104,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
}
// Try to read length of value
if (encoded.readableBytes() < lengthFieldSize) {
if (encoded.readableBytes() < LENGTH_FIELD_SIZE) {
encoded.resetReaderIndex();
return;
}
@ -126,15 +118,10 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
// SPDY/3 allows zero-length (empty) header values
if (valueLength == 0) {
if (version < 3) {
frame.setInvalid();
return;
} else {
frame.addHeader(name, "");
numHeaders --;
this.headerSize = headerSize;
continue;
}
frame.addHeader(name, "");
numHeaders --;
this.headerSize = headerSize;
continue;
}
headerSize += valueLength;

View File

@ -23,12 +23,11 @@ import java.util.zip.Inflater;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
class SpdyHeaderBlockZlibDecoder extends SpdyHeaderBlockRawDecoder {
final class SpdyHeaderBlockZlibDecoder extends SpdyHeaderBlockRawDecoder {
private final byte[] out = new byte[8192];
private final Inflater decompressor = new Inflater();
private ChannelBuffer decompressed;
private final ChannelBuffer decompressed = ChannelBuffers.buffer(4096);
public SpdyHeaderBlockZlibDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
super(spdyVersion, maxHeaderSize);
@ -36,52 +35,64 @@ class SpdyHeaderBlockZlibDecoder extends SpdyHeaderBlockRawDecoder {
@Override
void decode(ChannelBuffer encoded, SpdyHeadersFrame frame) throws Exception {
setInput(encoded);
int len = setInput(encoded);
int numBytes;
do {
numBytes = decompress(frame);
} while (!decompressed.readable() && numBytes > 0);
if (decompressor.getRemaining() != 0) {
throw new SpdyProtocolException("client sent extra data beyond headers");
}
encoded.skipBytes(len);
}
private void setInput(ChannelBuffer compressed) {
byte[] in = new byte[compressed.readableBytes()];
compressed.readBytes(in);
decompressor.setInput(in);
private int setInput(ChannelBuffer compressed) {
int len = compressed.readableBytes();
if (compressed.hasArray()) {
decompressor.setInput(compressed.array(), compressed.arrayOffset() + compressed.readerIndex(), len);
} else {
byte[] in = new byte[len];
compressed.getBytes(compressed.readerIndex(), in);
decompressor.setInput(in, 0, in.length);
}
return len;
}
private int decompress(SpdyHeadersFrame frame) throws Exception {
if (decompressed == null) {
decompressed = ChannelBuffers.dynamicBuffer(8192);
}
byte[] out = decompressed.array();
int off = decompressed.arrayOffset() + decompressed.writerIndex();
try {
int numBytes = decompressor.inflate(out);
int numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
if (numBytes == 0 && decompressor.needsDictionary()) {
decompressor.setDictionary(SPDY_DICT);
numBytes = decompressor.inflate(out);
numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
}
if (frame != null) {
decompressed.writeBytes(out, 0, numBytes);
decompressed.writerIndex(decompressed.writerIndex() + numBytes);
super.decode(decompressed, frame);
decompressed.discardReadBytes();
}
return numBytes;
} catch (DataFormatException e) {
throw new SpdyProtocolException(
"Received invalid header block", e);
throw new SpdyProtocolException("Received invalid header block", e);
}
}
@Override
void reset() {
decompressed = null;
decompressed.clear();
super.reset();
}
@Override
public void end() {
decompressed = null;
decompressed.clear();
decompressor.end();
super.end();
}

View File

@ -24,7 +24,6 @@ import org.jboss.netty.buffer.ChannelBuffers;
class SpdyHeaderBlockZlibEncoder extends SpdyHeaderBlockRawEncoder {
private final byte[] out = new byte[8192];
private final Deflater compressor;
private boolean finished;
@ -39,20 +38,36 @@ class SpdyHeaderBlockZlibEncoder extends SpdyHeaderBlockRawEncoder {
compressor.setDictionary(SPDY_DICT);
}
private void setInput(ChannelBuffer decompressed) {
byte[] in = new byte[decompressed.readableBytes()];
decompressed.readBytes(in);
compressor.setInput(in);
private int setInput(ChannelBuffer decompressed) {
int len = decompressed.readableBytes();
if (decompressed.hasArray()) {
compressor.setInput(decompressed.array(), decompressed.arrayOffset() + decompressed.readerIndex(), len);
} else {
byte[] in = new byte[len];
decompressed.getBytes(decompressed.readerIndex(), in);
compressor.setInput(in, 0, in.length);
}
return len;
}
private void encode(ChannelBuffer compressed) {
int numBytes = out.length;
while (numBytes == out.length) {
numBytes = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH);
compressed.writeBytes(out, 0, numBytes);
while (compressInto(compressed)) {
// Although unlikely, it's possible that the compressed size is larger than the decompressed size
compressed.ensureWritableBytes(compressed.capacity() << 1);
}
}
private boolean compressInto(ChannelBuffer compressed) {
byte[] out = compressed.array();
int off = compressed.arrayOffset() + compressed.writerIndex();
int toWrite = compressed.writableBytes();
int numBytes = compressor.deflate(out, off, toWrite, Deflater.SYNC_FLUSH);
compressed.writerIndex(compressed.writerIndex() + numBytes);
return numBytes == toWrite;
}
@Override
public synchronized ChannelBuffer encode(SpdyHeadersFrame frame) throws Exception {
if (frame == null) {
@ -68,9 +83,11 @@ class SpdyHeaderBlockZlibEncoder extends SpdyHeaderBlockRawEncoder {
return ChannelBuffers.EMPTY_BUFFER;
}
ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
setInput(decompressed);
ChannelBuffer compressed = ChannelBuffers.dynamicBuffer(decompressed.readableBytes());
int len = setInput(decompressed);
encode(compressed);
decompressed.skipBytes(len);
return compressed;
}